本文全面总结了 Docker 容器化技术的核心知识。从基础概念、底层原理、文件系统到容器网络通信,进一步扩展至 Dockerfile 编写、Docker Compose 与 Swarm 编排编排技术,并结合 Zabbix 与 MinIO 演示了实际应用部署。
1. 简介 docker Linux 容器 不是模拟一个完整的操作系统,而是对进程进行隔离。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离
Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口
Docker 将应用程序与该程序的依赖,打包在一个文件(image )里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样.
容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。
image 文件可以看作是容器的模板。Docker 根据 image 文件生成容器的实例。同一个 image 文件,可以生成多个同时运行的容器实例
容器用 Namespace 加 Cgroups 做隔离,默认互相看不到对方的进程和文件系统,而且性能开销极低。不过这层隔离比虚拟机弱得多——所有容器共享宿主内核,/proc、/sys 也没有完全隔离,默认同一 bridge 网络下容器之间还是互通的,细节见后面 Namespace 那节的”隔离得不彻底”。
Docker 从 17.03 版本之后分为 CE(Community Edition: 社区版) 和 EE(Enterprise Edition: 企业版)
docker 用途
简化环境搭建
简化运维工作量
微服务利器
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
⚠️ 注:get.daocloud.io/docker 这一国内安装源已失效,请改用官方脚本 get.docker.com 或所在发行版的官方仓库安装。
快速确认 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"
docker run : 启动容器
debian : 使用的镜像名称 (本地如果没有镜像,就在docker hub 进行搜素,并下载最新版)
echo “hello world” : 执行的命令
1 2 docker run -i -t debian /bin/bash docker run -i -t debian # 也可以不加 /bin/bash
-i -t : 附有一个 tty 的交互会话 -i 支持stdin , -t 终端或伪终端
/bin/bash : 获得一个 bash shell
退出 shell, 容器就会停止 , Ctrl+ P + Q 退出而不停止
容器生命周期
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 把权限禁掉了。正确做法是给挂载点加标签,让 Docker 自己打 SELinux label:-v /host/dir:/data:Z(该容器私有)或 :z(多容器共享);也可以在宿主上 chcon -Rt svirt_sandbox_file_t /host/dir。
不要用 --privileged=true 绕过去——那等于把几乎全部 capability、所有设备访问权限都给了容器,并关掉 seccomp/AppArmor,为开一扇门拆掉整面墙,和后面”Docker 安全 / 最小特权原则”那一节是直接冲突的;
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 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 ... ... ...
创建容器并指定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": "", ....
删除网络docker network rm extnetwork
限制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:password@<PUBLIC_IP>:9870" Environment="https_proxy=http://user:password@<PUBLIC_IP>: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
开启Linux子系统,以管理员权限 打开 PowerShell
1 2 3 4 ## 启用虚拟机平台 Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform ## 开启Linux子系统 Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
开启开发者模式,WIN + S 搜索 开发者设置,打开后选择 开发者模式
WIN + S 搜索 store, 打开 Microsoft Store(微软应用商店),eg: 搜索 Ubuntu ,选择 Ubuntu 18.04 LST 进行安装
待安装完成后,从应用中打开 Ubuntu 18.04 LST
优化
换源
打开自动切换到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
⚠️ 注:apt-key add 在新版 Debian/Ubuntu(及新版 apt)中已废弃,应改用 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg,再在 apt 源中通过 signed-by=/etc/apt/keyrings/docker.gpg 引用该密钥。
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 是利用 Linux 的 Namespace 、Cgroups 和联合文件系统三大机制来保证实现的, 所以它的原理是使用 Namespace 做主机名、网络、PID 等资源的隔离,使用 Cgroups 对进程或者进程组做资源(例如:CPU、内存等)的限制,联合文件系统用于镜像构建和容器运行环境.
一个“容器”,实际上是一个由 Linux Namespace、Linux Cgroups 和 rootfs 三种技术构建出来的进程的隔离环境。
一个正在运行的 Linux 容器,其实可以被“一分为二”地看待:
一组联合挂载在 /var/lib/docker/aufs/mnt 上的 rootfs,这一部分我们称为“容器镜像”(Container Image),是容器的静态视图;
一个由 Namespace+Cgroups 构成的隔离环境,这一部分我们称为“容器运行时”(Container Runtime),是容器的动态视图。
rootfs 里面打包的不只是应用,而是整个操作系统的文件和目录,也就意味着,应用和它运行所需要的所有依赖,都被封装在一起。(所有容器共享宿主机内核)
补 Kata Containers 与 gVisor 实现的方式与 Docker 不同
这两种容器实现的本质,都是给进程分配了一个独立的操作系统内核,从而避免了让容器共享宿主机的内核。这样,容器进程能够看到的攻击面,就从整个宿主机内核变成了一个极小的、独立的、以容器为单位的内核,从而有效解决了容器进程发生“逃逸”或者夺取整个宿主机的控制权的问题。
Kata Containers : 使用的是传统的虚拟化技术,通过虚拟硬件模拟出了一台“小虚拟机”,然后在这个小虚拟机里安装了一个裁剪后的 Linux 内核来实现强隔离。
gVisor 直接用 Go 语言“模拟”出了一个运行在用户态的操作系统内核,然后通过这个模拟的内核来代替容器进程向宿主机发起有限的、可控的系统调用。
Namespace 基于 Linux Namespace 的隔离机制相比于虚拟化技术也有很多不足之处,其中最主要的问题就是:隔离得不彻底。
Namespace 是 Linux 内核的一项功能,该功能对内核资源进行隔离,使得容器中的进程都可以在单独的命名空间中运行,并且只可以访问当前容器命名空间的资源。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 使用联合文件系统为容器提供构建层,使得容器可以实现写时复制以及镜像的分层构建和存储。常用的联合文件系统有 AUFS、OverlayFS 等。Docker 的存储驱动 除了这两个基于 UnionFS 的,还有基于块设备快照的 devicemapper(走的是 thin provisioning + 快照,不是联合挂载)、以及基于 btrfs/zfs 子卷的驱动。
Docker 在镜像的设计中,引入了层(layer)的概念。也就是说,用户制作镜像的每一步操作,都会生成一个层,也就是一个增量 rootfs。 镜像层是只读的,要修改里面的文件时,联合文件系统会先把它从只读层复制到容器的可读写层 (upperdir)再改,这就是 copy-on-write;只读层始终保持不变。也正因如此,docker commit 提交的只是读写层的增量。
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 会把主机上的目录直接映射到容器的指定目录下,实现数据持久化
分类
AUFS : 现在很少使用
Devicemapper : 常用在 Red Hat 或 CentOS 系统中
OverlayFS : Docker 目前推荐选择
Devicemapper Docker 的 Devicemapper 模式有两种:
loop-lvm : 主要用来开发和测试使用
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 上。
映射设备 通过 映射表 关联到具体的 物理目标设备 。事实上,映射设备不仅可以通过映射表关联到物理目标设备,也可以关联到虚拟目标设备,然后虚拟目标设备再通过映射表关联到物理目标设备。
瘦供给(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 # 是容器的联合挂载点目录,未生成容器时,该目录为空,而容器存在时,该目录下的内容跟容器中一致
这个 Ubuntu 镜像一共有四层,每一层镜像都是下一层的快照,镜像的最底层是基础设备的快照。当容器运行时,容器是基于镜像的快照。Devicemapper 实现镜像分层的根本原理就是快照
快照是数据在某一个时间点的存储状态。快照的主要作用是对数据进行备份,当存储设备发生故障时,可以使用已经备份的快照将数据恢复到某一个时间点,而 Docker 中的数据分层存储也是基于快照实现的。
overlay2 要想使用 overlay2,Docker 版本必须高于 17.06.02。
如果你的操作系统是 RHEL 或 CentOS,Linux 内核版本必须使用 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 $ echo "/dev/vdb1 /var/lib/docker xfs defaults,pquota 0 0" | sudo tee -a /etc/fstab # 重定向由当前 shell 执行,sudo echo ... >> 只对 echo 提权,写 /etc/fstab 仍然会 Permission denied $ 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
原理 overlay2 和 AUFS 类似,它将所有目录称之为层(layer),overlay2 的目录是镜像和容器分层的基础,而把这些层统一展现到同一的目录下的过程称为联合挂载(union mount)。overlay2 把目录的下一层叫作 lowerdir,上一层叫作 upperdir,联合挂载后的结果叫作 merged。
overlay2 将镜像层和容器层都放在单独的目录,并且有唯一 ID ,每一层仅存储发生变化的文件,最终使用联合挂载技术将容器层和镜像层的所有文件统一挂载到容器中,使得容器中看到完整的系统文件。
AUFS 配置
1 2 3 { "storage-driver": "aufs" }
每一个目录在 AUFS 中都叫作分支,而在 Docker 中则称之为层(layer),但最终呈现给用户的则是一个普通单层的文件系统,我们把多层以单一层的方式呈现出来的过程叫作联合挂载。
5. 容器间的通信 docker run 创建 d dr ocker 容器时,可以用 –net 选项指定容器的网络模式, Docker 有以下4 种网络模式: bridge 模式:使 –net =bridge 指定,默认设置, 容器启动时由 Docker 内置的 IPAM 从该 bridge 网络的子网里分配一个空闲 IP,直接写进容器的网络栈(--ip 可以指定),整个过程没有 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://<PUBLIC_IP>:5000/v2/_catalog # 本机IP地址 <PUBLIC_IP> --2020-02-20 15:27:49-- http://<PUBLIC_IP>:5000/v2/_catalog Connecting to <PUBLIC_IP>: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": ["<PUBLIC_IP>:5000"] 写入到 daemon.json文件 ## 信任私有仓库 ## systemctl restart docker # 重启docker
测试 标记镜像为私有仓库的镜像docker tag imwl/test <PUBLIC_IP>:5000/debiantest
上传镜像到私有仓库docker push <PUBLIC_IP>:5000/debiantest
7. Docker安全 Docker 是基于 Linux 内核的 Namespace 技术实现资源隔离的,所有容器都共享主机的内核
尽管目前 Namespace 已经提供了非常多的资源隔离类型,但是仍然有部分关键内容没有完全隔离。其中包括一些系统的关键性目录(eg: /sys /proc)
安全问题解决
User namespace : 主要用来做容器内用户和主机的用户隔离
保障镜像安全: 私有镜像进行安全检查,拉取镜像使用 https 和受信任的镜像仓库
及时升级宿主机内核
使用安全加固组件 : selinux , AppArmor(控制用户的访问权限), GRSecurity(只能访问控制,提供内存破坏防御,文件系统增强等多种防御形式)。
进行资源限制
使用安全容器 (每个容器都运行在一个单独的微型虚拟机中,拥有独立的操作系统和内核)(eg: Kata Container)
及时升级 docker 组件
8. DockerFile示例 简介 Dockerfile 是由一系列命令和参数构成的脚本,这些命令应用于操作系统基础镜像并最终创建的一个新镜像;
生产实践中一定优先使用 Dockerfile 的方式构建镜像。
基础镜像体积应该尽量小
只有 RUN、COPY、ADD 会新增镜像层,ENV/LABEL/EXPOSE/CMD/ENTRYPOINT/WORKDIR/USER/ARG 只写元数据、不增加体积。所以优化的重点是合并 RUN,以及把变动频繁的 COPY 放到后面以利用缓存,而不是去合并 ENV、LABEL 这些完全没有收益的行。
Dockerfile 书写原则
单一职责:由于容器的本质是进程,一个容器代表一个进程,因此不同功能的应用应该尽量拆分为不同的容器,每个容器只负责单一业务进程。
提供注释信息:Dockerfile 也是一种代码,我们应该保持良好的代码编写习惯,晦涩难懂的代码尽量添加注释,让协作者可以一目了然地知道每一行代码的作用,并且方便扩展和使用。
保持容器最小化:应该避免安装无用的软件包,可以加快容器构建速度,而且可以避免镜像体积过大。
合理选择基础镜 :容器的核心是应用,因此只要基础镜像能够满足应用的运行环境即可
使用 .dockerignore 文件 : 使用 .dockerignore 在构建时,忽略一些不需要参与构建的文件,从而提升构建效率。
尽量使用构建缓存 :Docker 构建过程中,每一条 Dockerfile 指令都会提交为一个镜像层,下一条指令都是基于上一条指令构建的。如果构建时发现要构建的镜像层的父镜像层已经存在,并且下一条命令使用了相同的指令,即可命中构建缓存。
正确设置时区: 从 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
使用国内软件源加快镜像构建速度: 最好的方式是换源
最小化镜像层数
常用命令 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 .
补充说明 cmd 与 ENTRYPOINT
CMD 和 ENTRYPOINT 的基本使用格式分为两种:
第一种为 CMD/ENTRYPOINT["command" , "param"] 。这种格式是使用 Linux 的 exec 实现的, 一般称为 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 Compose 是 Docker 官方的单机多容器管理系统,它本质是一个 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
访问
10. docker-swarm浅尝 1.12 版本以后自带
管理节点: 管理节点负责接受用户的请求,用户的请求中包含用户定义的容器运行状态描述,然后 Swarm 负责调度和管理容器,并且努力达到用户所期望的状态。(ratf 奇数节点)
工作节点: 工作节点运行执行器(Executor)负责执行具体的容器管理任务(Task),例如容器的启动、停止、删除等操作。
其他节点加入
1 docker swarm join --token <YOUR_TOKEN> <PUBLIC_IP>: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
验证
启用 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:9000 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 下类似
网页 查看
使用 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
连接公有云的oss 连接 oracle cloud 对象存储
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