一 、Docker简介及部署方法
1 Docker简介
Docker之父Solomon Hykes:Docker就好比传统的货运集装箱
2008 年LXC(LinuXContiainer)发布,但是没有行业标准,兼容性非常差 docker2013年首次发布,由Docker, Inc开发
1.1 什么是docker?

- Docker是管理容器的引擎,为应用打包、部署平台,而非单纯的虚拟化技术
- 它具有以下几个重要特点和优势:
-
- 轻量级虚拟化:Docker 容器相较于传统的虚拟机更加轻量和高效,能够快速启动和停止,节省系统资源。 例如,启动一个 Docker 容器可能只需要几秒钟,而启动一个虚拟机则可能需要几分钟。
-
- 一致性:确保应用程序在不同的环境中(如开发、测试、生产)具有一致的运行表现。 无论在本地还是云端,应用的运行环境都能保持相同,减少了因环境差异导致的问题。
-
- 可移植性:可以轻松地将 Docker 容器从一个平台迁移到另一个平台,无需担心依赖和环境配置的差 异。 比如,在本地开发的容器可以无缝部署到云服务器上。
-
- 高效的资源利用:多个 Docker 容器可以共享主机的操作系统内核,从而更有效地利用系统资源。
-
- 易于部署和和扩展:能够快速部署新的应用实例,并且可以根据需求轻松地进行水平扩展。
总之,Docker 极大地简化了应用程序的开发、部署和管理流程,提高了开发效率和运维的便利性。 它在现代软件开发和云计算领域得到了广泛的应用。
1.2 docker在企业中的应用场景

- 在企业中docker作为业务的最小载体而被广泛应用
- 通过docker企业可以更效率的部署应用并更节省资源
IaaS(Infrastructure as a Service),即基础设施即服务
PaaS是(Platform as a Service)即指平台即服务
SaaS(Software as a Service)软件运营服务是
1.3 docker与虚拟化的对比

| 虚拟机 | Docker 容器 | |
|---|---|---|
| 操作系统 | 宿主机上运行虚拟机 OS | 共享宿主机 OS |
| 存储 | 镜像较大(GB) | 镜像小(MB) |
| 性能 | 操作系统额外的 CPU、内存消耗 | 几乎无性能损耗 |
| 移植性 | 笨重、与虚拟化技术耦合度高 | 轻量、灵活迁移 |
| 隔离性 | 完全隔离 | 安全隔离 |
| 部署 | 慢、分钟级 | 快速、秒级 |
| 运行密度 | 一般几十个 | 单机支持上千容器 |
1.4 docker的优势
- 对于开发人员:Build once、Run anywhere,统一环境,消除环境差异,一次构建多环境运行。
- 对于运维人员:Configure once、Run anything,标准化部署,资源轻量、快速部署,便于迁移与自动化运维。
2 部署docker
2.1 容器工作方法

2.2 部署容器
2.2.1 配置软件仓库并安装docker-ce
bash
# 利用阿里云部署软件仓库
[root@docker-node1 ~]# cd /etc/yum.repos.d
[root@docker-node1 yum.repos.d]# cat docker.repo
[docker]
name=docker-ce
baseurl=https://mirrors.aliyun.com/docker-ce/linux/rhel/9/x86_64/stable
gpgcheck=0
[root@docker-node1 ~]# dnf makecache
[root@docker-node1 ~]# dnf remove podman -y
[root@docker-node1 ~]# dnf install docker-ce -y
[root@docker-node1 ~]# vim /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --iptables=true
[root@docker-node1 ~]# echo br_netfilter > /etc/modules-load.d/docker_mod.conf
[root@docker-node1 ~]# modprobe -a br_netfilter
[root@docker-node1 ~]# cat > /etc/sysctl.d/docker.conf <<EOF
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
[root@docker-node1 ~]# sysctl --system
[root@docker-node1 ~]# systemctl enable --now docker
2.2.2 查看是否安装成功

2.2.3 配置docker加速
bash
[root@docker-node1 ~]# cat > /etc/docker/daemon.json <<EOF
{
"registry-mirrors": ["https://docker.1ms.run"]
}
EOF
[root@docker-node1 ~]# systemctl restart docker
[root@docker-node1 ~]# docker info
Registry Mirrors:
https://docker.1ms.run/
二 、Docker的基本操作
1 Docker镜像管理
1.1 搜索镜像
bash
[root@Docker-node1 ~]# docker search nginx
| 参数 | 说明 |
|---|---|
| NAME | 镜像名称 |
| DESCRIPTION | 镜像说明 |
| STARS | 点赞数量 |
| OFFICIAL | 是否是官方的 |
1.2 拉取镜像
bash
# 从镜像仓库中拉取镜像
[root@docker-node1 ~]# docker pull busybox
[root@docker-node1 ~]# docker pull nginx:1.26-alpine #alpine 版本:nginx镜像的最小安装发型版本
# 查看本地镜像
[root@docker-node1 ~]# docker images
1.3 查看镜像信息
bash
[root@docker-node1 ~]# docker image inspect nginx
1.4 导出镜像
bash
# 保存镜像
[root@docker-node1 ~]# docker image save nginx:latest -o nginx-latest.tar.gz
[root@docker-node1 ~]# docker image save nginx:latest nginx:1.26-alpine -o nginx.tag.gz
# 保存所有镜像
[root@docker-node1 ~]# docker save `docker images | awk 'NR>1{print $1":"$2}'` -
o images.tar.gz
-o:指定导出镜像的位置;
可以同时导出多个镜像到一个文件中;
指定.tar.gz 可以导出并压缩。
1.5 删除镜像
bash
[root@docker-node1 ~]# docker rmi nginx:latest
[root@docker-node1 ~]# docker rmi `docker images | awk 'NR>1{print $1":"$2}'`
# 删除所有镜像
docker rm -f $(docker ps -aq)
2 容器的常用操作
2.1 启动容器
bash
[root@docker-node1 ~]# docker run -d --name nginx -p 80:80 nginx:latest
[root@docker-node1 ~]# docker run -it --name nginx -p 80:80 nginx:latest
# 进入到容器中,按<ctrl>+<d>退出并停止容器,# 按<ctrl>+<pq>退出但不停止容器
# 重新进入容器
[root@docker-node1 ~]# docker attach nginx
# 在容器中执行命令
[root@docker ~]# docker exec -it test ifconfig
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
-d #后台运行
-i #交互式运行
-t #打开一个终端
--name #指定容器名称
-p #端口映射
-p 80:8080 #把容器8080端口映射到本机80端口
--rm #容器停止自动删除容器
--network #指定容器使用的网络
2.2 查看容器运行信息
bash
[root@docker-node1 ~]# docker ps # 查看当前运行容器
[root@docker-node1 ~]# docker ps -a # 查看所有容器
[root@docker-node1 ~]# docker inspect busybox # 查看容器运行的详细信息
2.3 停止和运行容器
bash
[root@docker-node1 ~]# docker stop busybox # 停止容器
[root@docker-node1 ~]# docker kill busybox # 杀死容器,可以使用信号
[root@docker-node1 ~]# docker start busybox # 开启停止的容器
容器内的第一个进程必须一直处于运行的状态,否则这个容器,就会处于退出状态!
2.4 删除容器
bash
[root@docker-node1 ~]# docker rm centos7 # 删除停止的容器
[root@docker-node1 ~]# docker rm -f busybox # 删除运行的容器
[root@docker-node1 ~]# docker container prune -f # 删除所有停止的容器
2.5 容器内容提交
默认情况下,容器被删除后,在容器中的所有操作都会被清理,包括要保存的文件。如果想永久保存,那么我们需要把动作提交,提交后会生成新的镜像。当我们在运行新镜像后即可看到我们提交的内容
bash
[root@docker-node1 docker]# docker run -it --name test busybox
/ # touch leefile # 在容器中建立文件
/ # ls
bin etc leefile lib64 root tmp var
dev home lib proc sys usr
/ #
[root@docker-node1 docker]# docker rm test # 删掉容器后
test
[root@docker-node1 docker]# docker run -it --name test busybox # 删掉容器后开启新的容器文件不存在
/ # ls
bin dev etc home lib lib64 proc root sys tmp usr var
/ #
[root@docker-node1 ~]# docker commit -m "add leefile" test busybox:v1
sha256:648560550edc2b23a2176009ad9751db745c89df0f46d169f1aea249f9387e8f
[root@docker-node1 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
busybox v1 648560550edc 6 seconds ago 4.26MB
[root@docker-node1 ~]# docker image history busybox:v1
IMAGE CREATED CREATED BY SIZE COMMENT
648560550edc 34 seconds ago sh 3B add leefile
65ad0d468eb1 15 months ago BusyBox 1.36.1 (glibc), Debian 12 4.26MB
此方法不利于企业审计,所以不推荐使用,在企业中我们多用Dockerfile来构建镜像
2.6 系统中的文件和容器中的文件传输
bash
# 把容器中的文件复制到本机
[root@docker-node1 ~]# docker cp test2:/leefile /mnt
Successfully copied 1.54kB to /mnt
# 把本机文件复制到容器中
[root@docker-node1 ~]# docker cp /etc/fstab test2:/fstab
2.7 查询容器内部日志
bash
[root@docker-node1 ~]# docker logs
三、 docker镜像构建
1 docker镜像结构

- 共享宿主机的kernel
- base镜像提供的是最小的Linux发行版
- 同一docker主机支持运行多种Linux发行版
- 采用分层结构的最大好处是:共享资源
2 镜像运行的基本原理

- Copy-on-Write 可写容器层
- 容器层以下所有镜像层都是只读的
- docker从上往下依次查找文件
- 容器层保存镜像变化的部分,并不会对镜像本身进行任何修改
- 一个镜像最多127层
3 镜像获得方式
- 基本镜像通常由软件官方提供
- 企业镜像可以用官方镜像+Dockerfile来生成
- 系统关于镜像的获取动作有两种: docker pull 镜像地址
- docker load --i 本地镜像包
4 镜像构建
4.1 构建参数
| 指令 | 说明 |
|---|---|
| FROM | 指定 base 镜像 eg: FROM busybox:version |
| COPY | 复制文件 eg: COPY file /file 或者 COPY "file","/" |
| MAINTAINER | 指定作者信息,比如邮箱 eg: MAINTAINER user@example.com在最新版的 docker 中用 LABEL KEY="VALUE" 代替 |
| ADD | 功能和 copy 相似,指定压缩文件或 url eg: ADD test.tar/mnt 或者 eg: ADD http://ip/test.tar/mnt |
| ENV | 指定环境变量 eg: ENV FILENAME test |
| EXPOSE | 暴露容器端口 eg: EXPOSE 80 |
| VOLUME | 申明数据卷,通常指数据挂载点 eg: VOLUME "/var/www/html" |
| WORKDIR | 切换路径 eg: WORKDIR /mnt |
| RUN | 在容器中运行的指令 eg: touch file |
| CMD | 在启动容器时自动运行动作,可以被覆盖 eg: CMD echo FILENAME 会调用 shell 解析;eg: CMD \["/bin/sh","-c","echo FILENAME"] 不调用 shell 解析 |
| ENTRYPOINT | 和 CMD 功能和用法类似,但动作不可被覆盖 |
bash
# FROM COPY 和MAINTAINER
[root@docker-node1 ~]# mkdir docker/
[root@docker-node1 ~]# cd docker/
[root@docker-node1 docker]# touch leefile
[root@docker-node1 docker]# cat Dockerfile
FROM busybox:latest # 指定使用的基础镜像
MAINTAINER lee@xicheng.org # 指定作者信息
COPY leefile / # 复制当前目录文件到容器指定位置,leefile必须在当前目录中
[root@docker-node1 docker]# docker build -t example:v1 . # 构建镜像
# ADD
[root@docker-node1 docker]# touch file{1..3}
[root@docker-node1 docker]# tar zcf leefile.gz file*
[root@docker-node1 docker]# cat Dockerfile
FROM busybox:latest
MAINTAINER lee@xicheng.org
COPY leefile /
ADD leefile.gz /
[root@docker-node1 docker]# docker build -t example:v2 .
[root@docker-node1 docker]# docker run -it --rm --name test example:v2
/ # ls
bin etc file1 file3 lib proc sys usr
dev file file2 home lib64 root tmp var
# ENV CMD1
[root@docker-node1 docker]# cat Dockerfile
FROM busybox:latest
MAINTAINER lee@xicheng.org
ENV NAME lee
CMD echo $NAME
[root@docker-node1 docker]# docker build -t example:v3 .
[root@docker-node1 docker]# docker run -it --rm --name test example:v3
lee
# CMD2
[root@docker-node1 docker]# cat Dockerfile
FROM busybox:latest
MAINTAINER lee@xicheng.org
ENV NAME lee
#CMD echo $NAME
CMD ["/bin/echo", "$NAME"]
#CMD ["/bin/sh", "-c", "/bin/echo $NAME"]
[root@docker-node1 docker]# docker run -it --rm --name test example:v3 sh
lee
[root@docker-node1 docker]# docker rmi example:v3
# EXPOSE VOLUME VOLUME
[root@docker-node1 docker]# cat Dockerfile
FROM busybox:latest
MAINTAINER lee@xicheng.org
ENV NAME lee
EXPOSE 80 443
VOLUME /var/www/html
WORKDIR /var/www/html
RUN touch file
[root@docker-node1 docker]# docker build -t example:v4 .
[root@docker-node1 docker]# docker run -it --rm --name test example:v4
/var/www/html
4.2 Dockerfile实例
bash
# 一、建立构建目录,编写构建文件
[root@docker-node1 ~]# mdkir docker
[root@docker-node1 ~]# cd docker/
[root@docker-node1 docker]# cp ~/nginx-1.23.tar.gz .
[root@docker-node1 ~]# docker load -i /mnt/centos-7.tar.gz
Loaded image: centos:7
[root@docker-node1 docker]# cat Dockerfile
FROM centos:7
ADD nginx-1.23.tar.gz /mnt
WORKDIR /mnt/nginx-1.23
RUN yum install -y gcc make pcre-devel openssl-devel
RUN sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc
RUN ./configure --with-http_ssl_module --with-http_stub_status_module
RUN make
RUN make install
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]
# 二、通过dockerfile生成镜像
--------------------------------------------------------------------------------------
# 解决yum install报错
ERROR: failed to solve: process "/bin/sh -c yum install gcc make pcre-devel openssl-devel" did not complete successfully: exit code: 1
1.添加新sr1并挂载到centos镜像
2.下载web服务
[root@docker-node1 docker]# dnf install httpd -y
# 更改web端口
[root@docker-node1 docker]# vim /etc/httpd/conf/httpd.conf
[root@docker-node1 docker]# systemctl restart httpd
[root@docker-node1 docker]# mkdir -p /var/www/html/rhel7.9
[root@docker-node1 docker]# mount /dev/sr1 /var/www/html/rhel7.9/
mount: /var/www/html/rhel7.9: WARNING: source write-protected, mounted read-only.
#查看容器IP
[root@docker-node1 docker]# docker inspect centos
"IPAddress": "172.17.0.2",
[root@docker-node1 yum.repos.d]# docker run -it --name centos centos:7
[root@4b97ad6eb719 /]# ls
anaconda-post.log bin dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
[root@4b97ad6eb719 /]# cd /etc/yum.repos.d/
[root@4b97ad6eb719 yum.repos.d]# ls
CentOS-Base.repo CentOS-Debuginfo.repo CentOS-Sources.repo CentOS-fasttrack.repo
CentOS-CR.repo CentOS-Media.repo CentOS-Vault.repo CentOS-x86_64-kernel.repo
[root@4b97ad6eb719 yum.repos.d]# rm -rf *
[root@4b97ad6eb719 yum.repos.d]# vi centos7.repo
[root@4b97ad6eb719 yum.repos.d]# cat centos7.repo
[centos7]
name=centos7
baseurl=http://172.17.0.1:8888/rhel7.9
gpgcheck=0
# 提交
[root@docker-node1 docker]# docker commit -m "add repo" centos centos:repo
sha256:8629c14342c7fc149b23f5fe6c23b2b93d67bba025503f49fac82b9de7c4ec70
[root@docker-node1 docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos repo 8629c14342c7 21 seconds ago 204MB
[root@4b97ad6eb719 yum.repos.d]# exit
exit
[root@docker-node1 yum.repos.d]# docker rm centos
centos
[root@docker-node1 docker]# vim Dockerfile
FROM centos:repo
ADD nginx-1.23.tar.gz /mnt
WORKDIR /mnt/nginx-1.23
RUN yum install gcc make pcre-devel openssl-devel -y
RUN sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc
RUN ./configure --preifx=/usr/local/nginx --with-http_ssl_module --with-http_stub_status_module
RUN make
RUN make install
EXPOSE 80 443
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]
--------------------------------------------------------------------------------------
[root@docker-node1 docker]# docker build -t webserver:v1 .
# 三、测试镜像可用性
[root@docker-node1 docker]# docker images webserver
[root@docker-node1 docker]# docker history webserver:v1
[root@docker-node1 docker]# docker run -d --name checkimage webserver
# 四、查看容器详情
[root@docker-node1 docker]# docker inspect web1
5 镜像优化方案
5.1 镜像优化策略
- 选择最精简的基础镜像
- 减少镜像的层数
- 清理镜像构建的中间产物
5.2 镜像优化示例
方法1:缩减镜像层
bash
[root@docker-node1 docker]# vim Dockerfile
FROM centos:7 AS build
ADD nginx-1.23.3.tar.gz /mnt
WORKDIR /mnt/nginx-1.23.3
RUN yum install -y gcc make pcre-devel openssl-devel && sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && ./configure --with-http_ssl_module --with-http_stub_status_module && make && make install && cd .. && rm -fr nginx1.23.3 && yum clean all
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]
[root@docker-node1 docker]# docker build -t webserver:v2 .
[root@docker-node1 docker]# docker images webserver
方法2:多阶段构建
bash
[root@docker-node1 docker]# vim Dockerfile
FROM centos:7 AS build
ADD nginx-1.23.3.tar.gz /mnt
WORKDIR /mnt/nginx-1.23.3
RUN yum install -y gcc make pcre-devel openssl-devel && sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && ./configure --with-http_ssl_module --with-http_stub_status_module && make && make install && cd .. && rm -fr nginx1.23.3 && yum clean all
FROM centos:7
COPY --from=build /usr/local/nginx /usr/local/nginx
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]
[root@docker-node1 docker]# docker build -t webserver:v3 .
[root@docker-node1 docker]# docker images webserver
方法3:使用最精简镜像
使用google提供的最精简镜像
下载地址:https://github.com/GoogleContainerTools/distroless
下载镜像:docker pull gcr.io/distroless/base
四 docker 镜像仓库的管理
1 什么是docker仓库

Docker 仓库(Docker Registry ) 是用于存储和分发 Docker 镜像的集中式存储库。
它就像是一个大型的镜像仓库,开发者可以将自己创建的 Docker 镜像推送到仓库中,也可以从仓库中拉取所需的镜像。
Docker 仓库可以分为公共仓库和私有仓库:
- 公共仓库,如 Docker Hub,任何人都可以访问和使用其中的镜像。许多常用的软件和应用都有在 Docker Hub 上提供的镜像,方便用户直接获取和使用。例如,您想要部署一个 Nginx 服务器,就可以从 Docker Hub 上拉取 Nginx 的镜像。
- 私有仓库则是由组织或个人自己搭建和管理的,用于存储内部使用的、不希望公开的镜像。比如,一家企业为其特定的业务应用创建了定制化的镜像,并将其存储在自己的私有仓库中,以保证安全性和控制访问权限。
通过 Docker 仓库,开发者能够方便地共享和复用镜像,加速应用的开发和部署过程。
2 docker hub

Docker Hub 是 Docker 官方提供的一个公共的镜像仓库服务。
它是 Docker 生态系统中最知名和广泛使用的镜像仓库之一,拥有大量的官方和社区贡献的镜像。
以下是 Docker Hub 的一些关键特点和优势:
- 丰富的镜像资源:涵盖了各种常见的操作系统、编程语言运行时、数据库、Web 服务器等众多应用 的镜像。 例如,您可以轻松找到 Ubuntu、CentOS 等操作系统的镜像,以及 MySQL、Redis 等数据库 的镜像。
- 官方支持:提供了由 Docker 官方维护的一些重要镜像,确保其质量和安全性。
- 社区贡献:开发者们可以自由上传和分享他们创建的镜像,促进了知识和资源的共享。
- 版本管理:对于每个镜像,通常都有多个版本可供选择,方便用户根据需求获取特定版本。
- 便于搜索:用户可以通过关键词轻松搜索到所需的镜像。
docker hub的使用方法
bash
#登陆官方仓库
[root@docker-node1 ~]# docker login
#登陆信息保存位置
[root@docker-node1 ~]# cat .docker/config.json
3 docker仓库的工作原理
仓库中的三个角色
index docker索引服务,负责并维护有关用户帐户、镜像的校验以及公共命名空间的信息。
registry docker仓库,是镜像和图表的仓库,它不具有本地数据库以及不提供用户认证,通过Index Auth service的Token的方式进行认证
Registry Client Docker充当registry客户端来维护推送和拉取,以及客户端的授权。
3.1 pull原理

镜像拉取分为以下几步:
- docker客户端向index发送镜像拉去请求并完成与index的认证
- index发送认证token和镜像位置给dockerclient
- dockerclient携带token和根据index指引的镜像位置取连接registry
- Registry会根据client持有的token跟index核实身份合法性
- index确认此token合法性
- Registry会根据client的请求传递镜像到客户端
3.2 push原理

镜像上传的步骤:
- client向index发送上传请求并完成用户认证
- index会发方token给client来证明client的合法性
- client携带index提供的token连接Registry
- Registry向index合适token的合法性
- index证实token的合法性
- Registry开始接收客户端上传过来的镜像
4 搭建docker的私有仓库
4.1 为什么搭建私有仓库
docker hub虽然方便,但是还是有限制
- 需要internet连接,速度慢
- 所有人都可以访问
- 由于安全原因企业不允许将镜像放到外网
好消息是docker公司已经将registry开源,我们可以快速构建企业私有仓库 地址:https://docs.docker.com/registry/deploying/
4.2 搭建简单的Registry仓库
bash
# 1.下载Registry镜像
[root@docker ~]# docker pull registry
# 2.开启Registry
[root@docker ~]# docker run -d -p 5000:5000 --restart=always --name registry
registry
# 3.上传镜像到仓库中
# 给要上传的经镜像打标签
[root@docker-node1 ~]# docker tag busybox:latest 172.25.250.100:5000/busybox:latest
# 如果速度慢可以用docker加速器
[root@docker-node1 ~]# cat /etc/docker/daemon.json
{
"insecure-registries" : ["http://172.25.250.100:5000"]
}
[root@docker-node1 ~]# systemctl daemon-reload
[root@docker-node1 ~]# systemctl start docker
# docker在上传的过程中默认使用https,但是我们并没有建立https认证需要的认证文件所以会报错
[root@docker-node1 ~]# docker push 172.25.250.100:5000/busybox:latest
The push refers to repository [172.25.250.100:5000/busybox]
d51af96cf93e: Pushed
latest: digest: sha256:28e01ab32c9dbcbaae96cf0d5b472f22e231d9e603811857b295e61197e40a9b size: 527
# 4.查看镜像上传
[root@docker-node1 ~]# curl 172.25.250.100:5000/v2/_catalog
{"repositories":["busybox"]}
4.3 为Registry提加密传输
bash
# 生成认证key和证书
[root@docker-node1 ~]# openssl req -newkey rsa:4096 \
-nodes -sha256 -keyout certs/exam.com.key \
-addext "subjectAltName = DNS:reg.exam.com" \ # 指定备用名称
-x509 -days 365 -out certs/exam.com.crt
# 启动registry仓库
[root@docker-node1 ~]# docker run -d -p 443:443 --restart=always --name registry \
> --name registry -v /opt/registry:/var/lib/registry \
> -v /root/certs:/certs \
> -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
> -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/exam.com.crt \
> -e REGISTRY_HTTP_TLS_KEY=/certs/exam.com.key registry
测试:
[root@docker-node1 ~]# docker push reg.exam.com/busybox:latest # docker客
户端没有key和证书
Error response from daemon: Get "https://reg.exam.com/v2/": tls: failed to
verify certificate: x509: certificate signed by unknown authority
# 为客户端建立证书
[root@docker-node1 ~]# mkdir /etc/docker/certs.d/reg.exam.com/ -p
[root@docker-node1 ~]# cp /root/certs/exam.com.crt /etc/docker/certs.d/reg.exam.com/ca.crt
[root@docker-node1 ~]# systemctl restart docker
# 标签问题
[root@docker-node1 ~]# docker push reg.exam.com/busybox:latest
The push refers to repository [reg.exam.com/busybox]
An image does not exist locally with the tag: reg.exam.com/busybox
[root@docker-node1 ~]# docker tag busybox reg.exam.com/busybox:latest
[root@docker-node1 ~]# docker push reg.exam.com/busybox:latest
# DNS问题
[root@docker-node1 ~]# docker push reg.exam.com/busybox:latest
The push refers to repository [reg.exam.com/busybox]
Get "https://reg.exam.com/v2/": dial tcp: lookup reg.exam.com on 114.114.114.114:53: no such host
[root@docker-node1 ~]# vim /etc/hosts
The push refers to repository [reg.exam.com/busybox]
d51af96cf93e: Pushed
latest: digest: sha256:28e01ab32c9dbcbaae96cf0d5b472f22e231d9e603811857b295e61197e40a9b size: 527
[root@docker-node1 ~]#
# 测试
[root@docker-node1 ~]# curl -k https://reg.exam.com/v2/_catalog
{"repositories":["busybox"]}
4.4 为仓库建立登陆认证
bash
# 安装建立认证文件的工具包
[root@docker-node1 ~]# dnf install httpd-tools -y
# 建立认证文件
[root@docker-node1 ~]# mkdir auth
[root@docker-node1 ~]# htpasswd -Bc auth/htpasswd lee # -B 强制使用最安全加密方式,
默认用md5加密
New password: redhat #密码
Re-type new password:
Adding password for user lee
# 添加认证到registry容器中
[root@docker-node1 ~]# docker run -d -p 443:443 --restart=always --name registry \
--name registry -v /opt/registry:/var/lib/registry \
-v /root/certs:/certs \
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/exam.com.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/exam.com.key \
-v /root/auth:/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
registry
[root@docker-node1 certs]# curl -k https://reg.exam.com/v2/_catalog -u lee:redhat
{"repositories":["busybox"]}
# 登陆测试
[root@docker-node1 ~]# docker login reg.exam.com
Username: lee
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credential-stores
Login Succeeded
当仓库开启认证后必须登陆仓库才能进行镜像上传
bash
# 未登陆情况下上传镜像
[root@docker ~]# docker push reg.timinglee.org/busybox
Using default tag: latest
The push refers to repository [reg.timinglee.org/busybox]
d51af96cf93e: Preparing
no basic auth credentials
# 未登陆情况下也不能下载
[root@docker-node2 ~]# docker pull reg.timinglee.org/busybox
Using default tag: latest
Error response from daemon: Head
"https://reg.timinglee.org/v2/busybox/manifests/latest": no basic auth
credentials
5 构建企业级私有仓库
下载软件包地址:https://github.com/goharbor/harbor/releases
Harbor 是由vmware公司开源的企业级 Docker Registry 项目。 它提供了以下主要功能和特点:
- 基于角色的访问控制(RBAC):可以为不同的用户和用户组分配不同的权限,增强了安全性和管理的灵活性。
- 镜像复制:支持在不同的 Harbor 实例之间复制镜像,方便在多个数据中心或环境中分发镜像。
- 图形化用户界面(UI):提供了直观的 Web 界面,便于管理镜像仓库、项目、用户等。
- 审计日志:记录了对镜像仓库的各种操作,有助于追踪和审查活动。
- 垃圾回收:可以清理不再使用的镜像,节省存储空间。
5.1 部署harbor
bash
# 安装harbor
[root@docker-node1 ~]# tar zxf harbor-offline-installer-v2.5.4.tgz
[root@docker-node1 ~]# cd harbor/
[root@docker-node1 harbor]# ls
common.sh harbor.v2.5.4.tar.gz harbor.yml.tmpl install.sh LICENSE prepare
[root@docker-node1 harbor]# cp harbor.yml.tmpl harbor.yml
# 建立认证并登录
[root@docker-node1 ~]# htpasswd -Bc auth/htpasswd lee
# 创建加密密钥
[root@docker-node1 ~]# openssl req -newkey rsa:4096 \
-nodes -sha256 -keyout certs/exam.com.key \
-addext "subjectAltName = DNS:reg.exam.com" \
-x509 -days 365 -out certs/exam.com.crt
[root@docker-node1 ~]# mkdir /etc/docker/certs.d/reg.exam.com/ -p
[root@docker-node1 ~]# cp /root/certs/exam.com.crt /etc/docker/certs.d/reg.exam.com/ca.crt
[root@docker-node1 ~]# systemctl restart docker
# 修改配置文件
[root@docker-node1 harbor]# vim harbor.yml
5 hostname: reg.exam.com
17 certificate: /data/certs/exam.com.crt
18 private_key: /data/certs/exam.com.key
34 harbor_admin_password: redhat
[root@docker-node1 harbor]# mkdir /data
[root@docker-node1 data]# cp /root/certs /data/certs -r
[root@docker-node1 harbor]# ./install.sh --with-chartmuseum
[root@docker-node1 harbor]# docker compose stop
[root@docker-node1 harbor]# docker compose up -d



上传镜像
bash
# 确保登录admin
[root@docker-node1 harbor]# docker login reg.exam.com
Authenticating with existing credentials...
Stored credentials invalid or expired
Username (lee): admin
Password: redhat
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credential-stores
Login Succeeded
# 打包镜像
[root@docker-node1 ~]# docker tag busybox:latest reg.exam.com/superlee/busybox:latest
[root@docker-node1 ~]# docker push reg.exam.com/superlee/busybox:latest
The push refers to repository
# 上传镜像
[reg.exam.com/superlee/busybox]
d51af96cf93e: Pushed
latest: digest: sha256:28e01ab32c9dbcbaae96cf0d5b472f22e231d9e603811857b295e61197e40a9b size: 527
五、 Docker 网络
docker的镜像是令人称道的地方,但网络功能还是相对薄弱的部分
docker安装后会自动创建3种网络:bridge、host、none
1 docker原生网络bridge
docker安装时会创建一个名为 docker0 的Linux bridge,新建的容器会自动桥接到这个接口
bash
[root@docker mnt]# ip link show type bridge
- bridge模式下容器没有一个公有ip,只有宿主机可以直接访问,外部主机是不可见的。
- 容器通过宿主机的NAT规则后可以访问外网

bash
# 更改内核参数
[root@docker-node1 ~]# grubby --update-kernel ALL --args iptables=true
[root@docker-node1 ~]# reboot
# 查看防火墙策略
[root@docker-node1 ~]# iptables -nL
# bridge
[root@docker-node1 harbor]# docker run -it --name webserver --network bridge busybox
/ # ifconfig
2 docker原生网络host
host网络模式需要在容器创建时指定 --network=host host模式可以让容器共享宿主机网络栈,这样的好处是外部主机与容器直接通信,但是容器的网络缺少隔离性

bash
[root@docker-node1 ~]# docker run -d --name web -p 80:80 nginx:1.23
a1b8d4fa9c9bfd870c4997d295efb10ff155e50a4a1942d9c99fabe32a02de55
[root@docker-node1 ~]# ifconfig
如果公用一个网络,那么所有的网络资源都是公用的,比如启动了nginx容器那么真实主机的80端口被占用,在启动第二个nginx容器就会失败
3 docker 原生网络none
none模式是指禁用网络功能,只有lo接口,在容器创建时使用 --network=none指定。
bash
[root@docker-node1 ~]# docker run -it --name test --rm --network none busybox
/ # ifconfig
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
4 docker的自定义网络
自定义网络模式,docker提供了三种自定义网络驱动: bridge、overlay、macvlan
- bridge驱动类似默认的bridge网络模式,但增加了一些新的功能
- overlay和macvlan是用于创建跨主机网络
建议使用自定义的网络来控制哪些容器可以相互通信,还可以自动DNS解析容器名称到IP地址。
4.1 自定义桥接网络
在建立自定以网络时,默认使用桥接模式
bash
[root@docker-node1 ~]# docker network create my_net1
[root@docker-node1 ~]# docker network ls
2644d485a93e my_net1 bridge local
桥接默认是单调递增
bash
[root@docker-node1 ~]# ifconfig
桥接也支持自定义子网和网关
bash
[root@docker-node1 ~]# docker network create my_net2 --subnet 172.25.0.0/24 --gateway 172.25.0.2
[root@docker-node1 ~]# docker network inspect my_net2
4.2 为什么要自定义桥接
核心问题:默认bridge网络的缺陷
- Docker默认bridge网桥,容器IP是动态分配,分配规则取决于容器启动先后顺序。容器重启后IP可能发生变化。
- 如果直接用IP进行多容器互相访问,IP一变服务就不通,不可靠。
- Docker原生默认bridge网络不自带DNS解析,无法直接使用容器名称通信。
自定义桥接(自定义网络)的优势
内置DNS解析 :同一自定义网络内,可以直接使用容器名称互相访问,不用依赖IP,稳定性强。
注意:
- 不同自定义网络之间默认隔离,不能直接通讯。
- 底层防火墙:RHEL7 用
iptables做网络隔离;RHEL9 使用nftables,可以用nft list ruleset查看隔离策略。#在rhel7中使用的是iptables进行网络隔离,在rhel9中使用nftpables
root@docker \~# nft list ruleset可以看到网络隔离策略
4.3 如何让不同的自定义网络互通?

bash
[root@docker-node1 ~]# docker run -d --name web1 --network my_net1 nginx
[root@docker-node1 ~]# docker run -it --name test --network my_net2 busybox
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:C0:A8:00:01
inet addr:192.168.0.1 Bcast:192.168.0.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:36 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:5244 (5.1 KiB) TX bytes:0 (0.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
/ # ping 172.18.0.2
PING 172.18.0.2 (172.18.0.2): 56 data bytes #此时ping不通
[root@docker ~]# docker network connect my_net1 test
# 在上面test容器中加入网络eth1
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:C0:A8:00:01
inet addr:192.168.0.1 Bcast:192.168.0.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:45 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:5879 (5.7 KiB) TX bytes:602 (602.0 B)
eth1 Link encap:Ethernet HWaddr 02:42:AC:12:00:03
inet addr:172.18.0.3 Bcast:172.18.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:15 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:2016 (1.9 KiB) TX bytes:0 (0.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:4 errors:0 dropped:0 overruns:0 frame:0
TX packets:4 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:212 (212.0 B) TX bytes:212 (212.0 B)
4.4 joined容器网络
Joined容器一种较为特别的网络模式,在容器创建时使用--network=container:vm1指定。(vm1指定的是运行的容器名)
处于这个模式下的 Docker 容器会共享一个网络栈,这样两个容器之间可以使用localhost高效快速通信。

bash
[root@docker-node1 ~]# docker run -it --rm --name test --network mynet1 busybox
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:12:00:02
inet addr:172.18.0.2 Bcast:172.18.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:17 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:2330 (2.2 KiB) TX bytes:0 (0.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
/ #
# 设置和test共用一个网卡
[root@docker-node1 ~]# docker run -it --name test1 --network container:test busybox
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:12:00:02
inet addr:172.18.0.2 Bcast:172.18.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:19 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:2470 (2.4 KiB) TX bytes:0 (0.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
4.5 joined网络示例演示
利用容器部署phpmyadmin管理mysql
bash
# 读取镜像
[root@docker-node1 ~]# docker load -i mysql-5.7.tar.gz
[root@docker-node1 ~]# docker load -i phpmyadmin-latest.tar.gz
# 运行phpmysqladmin
[root@docker ~]# docker run -d --name mysqladmin --network my_net1 \
-e PMA_ARBITRARY=1 \ # 在web页面中可以手动输入数据库地址和端口
-p 80:80 phpmyadmin:latest
# 运行数据库
[root@docker ~]# docker run -d --name mysql \
-e MYSQL_ROOT_PASSWORD='redhat' \ # 设定数据库密码
--network container:mysqladmin \ # 把数据库容器添加到phpmyadmin容器中
mysql:5.7

开启的phpmyadmin容器中是没有数据库的
这里填写的localhost:3306是因为mysql容器和phpmyadmin容器公用一个网络站
5. 容器内外网的访问
5.1 容器访问外网

- 在rhel7中,docker访问外网是通过iptables添加地址伪装策略来完成容器网文外网
- 在rhel7之后的版本中通过nftables添加地址伪装来访问外网
5.2 外网访问docker容器

端口映射 -p 本机端口:容器端口来暴漏端口从而达到访问效果
6 docker跨主机网络
在生产环境中,我们的容器不可能都在同一个系统中,所以需要容器具备跨主机通信的能力
跨主机网络解决方案:
- docker原生的overlay和macvlan
- 第三方的flannel、weave、calico
众多网络方案是如何与docker集成在一起的
- libnetwork docker容器网络库
- CNM (Container Network Model)这个模型对容器网络进行了抽象
6.1 CNM (Container Network Model)

CNM分三类组件
- Sandbox:容器网络栈,包含容器接口、dns、路由表。(namespace)
- Endpoint:作用是将sandbox接入network (veth pair)
- Network:包含一组endpoint,同一network的endpoint可以通信
5.6.2 macvlan网络方式实现跨主机通信
macvlan网络方式
- Linux kernel提供的一种网卡虚拟化技术。
- 无需Linux bridge,直接使用物理接口,性能极好
- 容器的接口直接与主机网卡连接,无需NAT或端口映射。
- macvlan会独占主机网卡,但可以使用vlan子接口实现多macvlan网络
- vlan可以将物理二层网络划分为4094个逻辑网络,彼此隔离,vlan id取值为1~4094
macvlan网络间的隔离和连通
- macvlan网络在二层上是隔离的,所以不同macvlan网络的容器是不能通信的
- 可以在三层上通过网关将macvlan网络连通起来
- docker本身不做任何限制,像传统vlan网络那样管理即可
示例:
bash
# 在两台docker主机上各添加一块网卡,打开网卡混杂模式
[root@docker-node1 ~]# ip link set eth1 promisc on
[root@docker-node1 ~]# ip link set up eth1
[root@docker-node1 ~]# ifconfig eth1
eth1: flags=4419<UP,BROADCAST,RUNNING,PROMISC,MULTICAST> mtu 1500
ether 00:0c:29:ce:7a:46 txqueuelen 1000 (Ethernet)
RX packets 75 bytes 4896 (4.7 KiB)
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
# 添加macvlan网路
[root@docker-node1 ~]# docker network create \
> -d macvlan \
> --subnet 1.1.1.0/24 \
> --gateway 1.1.1.1 \
> -o parent=eth1 macvlan1
88995f57ffef146a45e2ceff0e22d73ab3b38385e485e93976f14ee0f260fd4b
[root@docker-node1 ~]# docker run -it --name busybox --network macvlan1 --ip 1.1.1.100 --rm busybox/ #
/ # ping 1.1.1.200
PING 1.1.1.200 (1.1.1.200): 56 data bytes
64 bytes from 1.1.1.200: seq=0 ttl=64 time=1.038 ms
64 bytes from 1.1.1.200: seq=1 ttl=64 time=0.298 ms
64 bytes from 1.1.1.200: seq=2 ttl=64 time=0.354 ms
^C
--- 1.1.1.200 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.298/0.563/1.038 ms
/ #
# node2
[root@docker-node2 ~]# docker load -i busybox-latest.tar.gz
d51af96cf93e: Loading layer 4.495MB/4.495MB
Loaded image: busybox:latest
[root@docker-node2 ~]# docker run -it --name busybox --network macvlan1 --ip 1.1.1.200 --rm busybox/ #
/ # ping 1.1.1.100
PING 1.1.1.100 (1.1.1.100): 56 data bytes
64 bytes from 1.1.1.100: seq=0 ttl=64 time=0.756 ms
^C
--- 1.1.1.100 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.756/0.756/0.756 ms
六、 Docker 数据卷管理及优化

Docker 数据卷是一个可供容器使用的特殊目录,它绕过了容器的文件系统,直接将数据存储在宿主机上
这样可以实现以下几个重要的目的:
- 数据持久化:即使容器被删除或重新创建,数据卷中的数据仍然存在,不会丢失。
- 数据共享:多个容器可以同时挂载同一个数据卷,实现数据的共享和交互。
- 独立于容器生命周期:数据卷的生命周期独立于容器,不受容器的启动、停止和删除的影响
1 为什么要用数据卷
docker分层文件系统:性能差、生命周期与容器相同
docker数据卷
- mount到主机中,绕开分层文件系统
- 和主机磁盘性能相同,容器删除后依然保留
- 仅限本地磁盘,不能随容器迁移
docker提供了两种卷: bind mount和docker managed volume
2 bind mount 数据卷
- 是将主机上的目录或文件mount到容器里。
- 使用直观高效,易于理解。
- 使用 -v 选项指定路径格式 :
- -v选项指定的路径,如果不存在,挂载时会自动创建
bash
[root@docker-node1 ~]# docker run -it --rm \
> -v /tmp/data1:/data1 \
> -v /tmp/data1:/data2:ro \
> -v /etc/passwd:/data/passwd:ro busybox
/ # tail -n 3 /data/passwd
tcpdump:x:72:72::/:/sbin/nologin
redhat:x:1000:1000:redhat:/home/redhat:/bin/bash
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
/ # touch /data1/file1
/ # touch /data2/file1
touch: /data2/file1: Read-only file system
3 docker managed 数据卷
- bind mount必须指定host文件系统路径,限制了移植性
- docker managed volume 不需要指定mount源,docker自动为容器创建数据卷目录
- 默认创建的数据卷目录都在 /var/lib/docker/volumes 中
- 如果挂载时指向容器内已有的目录,原有数据会被复制到volume中
示例:
bash
[root@docker-node1 ~]# docker load -i mysql-5.7.tar.gz
[root@docker-node1 ~]# docker run -d --name mysql -e MYSQL_ROOT_PASSWORD='redhat' mysql:5.7
[root@docker-node1 volumes]# ls -l /var/lib/docker/volumes/
[root@docker-node1 volumes]# touch 39e99109425ea3322d99b73c5672e6414eca041bbafcdee3c8476d74874f788e/_data/file
[root@docker-node1 volumes]# docker exec -it mysql bash
bash-4.2# cd /var/lib/mysql
bash-4.2# ls
auto.cnf client-cert.pem ib_buffer_pool ibdata1 mysql.sock public_key.pem sys
ca-key.pem client-key.pem ib_logfile0 ibtmp1 performance_schema server-cert.pem
ca.pem file ib_logfile1 mysql private_key.pem server-key.pem
bash-4.2# pwd
/var/lib/mysql
# 清理未使用的 Docker 数据卷
# [root@docker ~]# docker volume prune
# 建立数据卷
[root@docker-node1 ~]# docker volume create vol1
vol1
[root@docker-node1 ~]# ls -l /var/lib/docker/volumes/vol1/_data/
total 0
# 查看卷
[root@docker-node1 ~]# docker volume ls
local vol1
# 使用建立的数据卷
[root@docker-node1 ~]# docker run -d --name web1 -p 80:80 -v vol1:/usr/share/nginx/html nginx
0ae5ae62f4c2d360731cb2106c7d6c9fadc28af491a05a406c7c1e5380576023
[root@docker-node1 ~]# cd /var/lib/docker/volumes/vol1/_data/
[root@docker-node1 _data]# ls
50x.html index.html
[root@docker-node1 _data]# echo vol1 > index.html
[root@docker-node1 _data]# curl 172.25.250.100
vol1
4 数据卷容器(Data Volume Container)
数据卷容器(Data Volume Container)是 Docker 中一种特殊的容器,主要用于方便地在多个容器之间共享数据卷。
bash
# 建立数据卷容器
[root@docker-node1 ~]# docker run -d --name datavol \
> -v /tmp/data1/:/data1:rw \
> -v /tmp/data2/:/data2:ro \
> -v /etc/resolv.conf:/etc/hosts busybox
# 使用数据卷容器
[root@docker-node1 ~]# docker run -it --name test --rm --volumes-from datavol busybox
/ # ls
bin data1 data2 dev etc home lib lib64 proc root sys tmp usr var
/ # cat /etc/resolv.conf
# Generated by Docker Engine.
# This file can be edited; Docker Engine will not make further changes once it
# has been modified.
nameserver 114.114.114.114
search exam.com
# Based on host file: '/etc/resolv.conf' (legacy)
# Overrides: []
/ # touch data1/file1
/ # touch data2/file1
touch: data2/file1: Read-only file system
/ #
5 bind mount 数据卷和docker managed 数据卷的对比
相同点: 两者都是 host 文件系统中的某个路径不同点
不同点:
| 对比维度 | bind mount | docker managed volume |
|---|---|---|
| volume 位置 | 可任意指定 | /var/lib/docker/volumes/... |
| 对已有 mount point 影响 | 隐藏并替换为 volume | 原有数据复制到 volume |
| 是否支持单个文件 | 支持 | 不支持,只能是目录 |
| 权限控制 | 可设置为只读,默认为读写权限 | 无控制,均为读写权限 |
| 移植性 | 移植性弱,与 host path 绑定 | 移植性强,无需指定 host 目录 |
6 备份与迁移数据卷
bash
# 备份数据卷
# 建立容器并指定使用卷到要备份的容器
[root@docker ~]# docker run --volumes-from datavol \
-v `pwd`:/backup busybox \ # 把当前目录挂在到容器中用于和容器交互保存要备份的容器
tar zcf /backup/data1.tar.gz /data1 # 备份数据到本地
# 数据恢复
docker run -it --name test -v leevol1:/data1 -v `pwd`:/backup busybox /bin/sh -c "tar zxf /backup/data1.tar.gz;/bin/sh"
/ # ls
backup data1 etc lib proc sys usr
bin dev home lib64 root tmp var
/ # cd data1/ # 查看数据迁移情况
/data1 # ls
index.html file1
七、 Docker 的安全优化
Docker容器的安全性,很大程度上依赖于Linux系统自身
评估Docker的安全性时,主要考虑以下几个方面:
- Linux内核的命名空间机制提供的容器隔离安全
- Linux控制组机制对容器资源的控制能力安全。
- Linux内核的能力机制所带来的操作权限安全 Docker程序(特别是服务端)本身的抗攻击性。
- 其他安全增强机制对容器安全性的影响
在rhel9中默认使用cgroup-v2 但是cgroup-v2中不利于观察docker的资源限制情况,所以推荐使用cgroup-v1
root@docker \~# grubby --update-kernel=/boot/vmlinuz-$(uname -r) \
--args="systemd.unified_cgroup_hierarchy=0
systemd.legacy_systemd_cgroup_controller"
1 命名空间隔离的安全
- 当docker run启动一个容器时,Docker将在后台为容器创建一个独立的命名空间。命名空间提供了 最基础也最直接的隔离。
- 与虚拟机方式相比,通过Linux namespace来实现的隔离不是那么彻底。
- 容器只是运行在宿主机上的一种特殊的进程,那么多个容器之间使用的就还是同一个宿主机的操作 系统内核。
- 在 Linux 内核中,有很多资源和对象是不能被 Namespace 化的,比如:磁盘等等
bash
[root@docker-node1 ~]# mount -t cgroup
[root@docker-node1 ~]# mount -t cgroup2
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot)
grubby --update-kernel=/boot/vmlinuz-$(uname -r) \
--args="systemd.unified_cgroup_hierarchy=0 systemd.legacy_systemd_cgroup_controller"
reboot
[root@docker-node1 ~]# docker run -d --name web nginx
[root@docker-node1 ~]# docker inspect web | grep Pid
[root@docker-node1 ~]# cd /proc/4678/ns/
[root@docker-node1 ns]# ls
cgroup ipc mnt net pid pid_for_children time time_for_children user uts
[root@docker-node1 ns]# ls -d
2 控制组资源控制的安全
- 当docker run启动一个容器时,Docker将在后台为容器创建一个独立的控制组策略集合。
- Linux Cgroups提供了很多有用的特性,确保各容器可以公平地分享主机的内存、CPU、磁盘IO等 资源。
- 确保当发生在容器内的资源压力不会影响到本地主机系统和其他容器,它在防止拒绝服务攻击(DDoS)方面必不可少
bash
[root@docker-node1 ~]# docker run -it --name test busybox #内存资源默认没有被
隔离
/ # free -m
total used free shared buff/cache available
Mem: 1743 396 834 6 513 1194
Swap: 4096 0 4096
/ # exit
[root@docker-node1 ~]# free -m
total used free shared buff/cache available
Mem: 1743 512 870 6 514 1231
Swap: 4095 0 4095
3 内核能力机制
- 能力机制(Capability)是Linux内核一个强大的特性,可以提供细粒度的权限访问控制。
- 大部分情况下,容器并不需要"真正的"root权限,容器只需要少数的能力即可。
- 默认情况下,Docker采用"白名单"机制,禁用"必需功能"之外的其他权限。
4 Docker服务端防护
- 使用Docker容器的核心是Docker服务端,确保只有可信的用户才能访问到Docker服务。
- 将容器的root用户映射到本地主机上的非root用户,减轻容器和主机之间因权限提升而引起的安全 问题。
- 允许Docker 服务端在非root权限下运行,利用安全可靠的子进程来代理执行需要特权权限的操作。 这些子进程只允许在特定范围内进行操作。
bash
[root@docker-node1 ~]# ls -ld /var/lib/docker/ # 默认docker是用root用户控制资源的
1 Docker的资源限制
Linux Cgroups 的全称是 Linux Control Group。
- 是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。
- 对进程进行优先级设置、审计,以及将进程挂起和恢复等操作。
Linux Cgroups 给用户暴露出来的操作接口是文件系统
- 它以文件和目录的方式组织在操作系统的 /sys/fs/cgroup 路径下。
- 执行此命令查看:mount -t cgroup
在 /sys/fs/cgroup 下面有很多诸如 cpuset、cpu、 memory 这样的子目录,也叫子系统。
在每个子系统下面,为每个容器创建一个控制组(即创建一个新目录)。
控制组下面的资源文件里填上什么值,就靠用户执行 docker run 时的参数指定。
1.1.限制cpu使用
1.限制cpu的使用量
bash
[root@docker-node1 ~]# docker load -i ubuntu-latest.tar.gz
f36fd4bb7334: Loading layer 80.56MB/80.56MB
Loaded image: ubuntu:latest
[root@docker-node1 ~]# docker run -it --rm --name test \
> --cpu-period 100000 \
> --cpu-quota 20000 ubuntu
root@59bb8bdc0225:/# dd if=/dev/zero of=/dev/null &
[1] 9
root@59bb8bdc0225:/# top
top - 07:13:26 up 20 min, 0 user, load average: 0.97, 0.51, 0.21
Tasks: 3 total, 2 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.7 us, 2.0 sy, 0.0 ni, 96.9 id, 0.0 wa, 0.2 hi, 0.2 si, 0.0 st
MiB Mem : 1743.5 total, 668.4 free, 550.4 used, 682.0 buff/cache
MiB Swap: 4096.0 total, 4096.0 free, 0.0 used. 1193.1 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
9 root 20 0 2736 1408 1408 R 19.7 0.1 0:09.68 dd
1 root 20 0 4588 3712 3200 S 0.0 0.2 0:00.01 bash
10 root 20 0 8848 5120 3072 R 0.0 0.3 0:00.00 top
# 在cgroup中查看docker的资源限制
[root@docker ~]# cat /sys/fs/cgroup/system.slice/docker573150b6c2c90a53f9d75df77455a526ff14e93fb7304674c80307c51882e605.scope/cpu.max 20000 100000
2.限制cpu的优先级
bash
# 关闭cpu的核心,当cpu都不空闲下才会出现争抢的情况,为了实验效果我们可以关闭一个cpu核心
[root@docker-node1 ~]# echo 0 > /sys/devices/system/cpu/cpu1/online
[root@docker-node1 ~]# cat /proc/cpuinfo
[root@docker-node1 ~]# docker run -it --rm --cpu-shares 100 ubuntu
root@5ecafc581a63:/# dd if=/dev/zero of=/dev/null &
[1] 9
root@5ecafc581a63:/# top
1.2 限制内存使用
bash
# 安装依赖性
libcgroup-0.41-19.el8.x86_64.rpm
libcgroup-tools-0.41-19.el8.x86_64.rpm
# 开启容器并限制容器使用内存大小
[root@docker-node1 ~]# docker run -d --name test --memory 200M --memory-swap 200M nginx
6010d0618fb82d8687b3d15179537a3fadc746bce745f816514f59c7122d1166
# 查看容器内存使用限制
[root@docker-node1 ~]# cd /sys/fs/cgroup/memory/docker/6010d0618fb82d8687b3d15179537a3fadc746bce745f816514f59c7122d1166/
[root@docker-node1 6010d0618fb82d8687b3d15179537a3fadc746bce745f816514f59c7122d1166]# cat memory.limit_in_bytes
209715200
[root@docker-node1 6010d0618fb82d8687b3d15179537a3fadc746bce745f816514f59c7122d1166]# cat memory.memsw.limit_in_bytes
209715200
# 测试容器内存限制,在容器中我们测试内存限制效果不是很明显,可以利用工具模拟容器在内存中写入数据
# 在系统中/dev/shm这个目录被挂在到内存中
[root@docker-node1 ~]# docker run -d --name test --memory 200M --memory-swap 200M nginx
415ee962d70e40bdac774b2869d5e4d6faa13479bd74608e7b1618e9d46327ee
[root@docker-node1 ~]# cgexec -g memory:docker/415ee962d70e40bdac774b2869d5e4d6faa13479bd74608e7b1618e9d46327ee dd if=/dev/zero of=/dev/shm/bigfile bs=1M count=150
150+0 records in
150+0 records out
157286400 bytes (157 MB, 150 MiB) copied, 0.53283 s, 295 MB/s
[root@docker-node1 ~]# cgexec -g memory:docker/415ee962d70e40bdac774b2869d5e4d6faa13479bd74608e7b1618e9d46327ee dd if=/dev/zero of=/dev/shm/bigfile bs=1M count=120
120+0 records in
120+0 records out
125829120 bytes (126 MB, 120 MiB) copied, 0.0695907 s, 1.8 GB/s
[root@docker-node1 ~]# cgexec -g memory:docker/415ee962d70e40bdac774b2869d5e4d6faa13479bd74608e7b1618e9d46327ee dd if=/dev/zero of=/dev/shm/bigfile bs=1M count=200
Killed
[root@docker-node1 ~]#
1.3 限制docker的磁盘io
bash
[root@docker-node1 ~]# docker run -it --rm \
--device-write-bps \ # 指定容器使用磁盘io的速率
/dev/nvme0n1:30M \ # /dev/nvme0n1是指定系统的磁盘,30M即每秒30M数据
ubuntu
root@149e8aba1b2f:/# dd if=/dev/zero of=bigfile # 开启容器后会发现速度和设定不匹配,是因为系统的缓存机制
^C1576748+0 records in
1576748+0 records out
807294976 bytes (807 MB, 770 MiB) copied, 5.85982 s, 138 MB/s
root@149e8aba1b2f:/# dd if=/dev/zero of=/bigfile bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB, 100 MiB) copied, 0.0558805 s, 1.9 GB/s
root@149e8aba1b2f:/# dd if=/dev/zero of=bigfile bs=1M count=100 oflag=direct # 设定dd命令直接写入磁盘
100+0 records in
100+0 records out
104857600 bytes (105 MB, 100 MiB) copied, 3.33922 s, 31.4 MB/s
2 Docker的安全加固
2.1 Docker默认隔离性
在系统中运行容器,我们会发现资源并没有完全隔离开
bash
[root@docker-node1 ~]# free -m
# 系统内存使用情况
total used free shared buff/cache available
Mem: 3627 1128 1714 207 1238 2498
Swap: 2062 0 2062
[root@docker-node1 ~]# docker run --rm --memory 200M -it ubuntu
root@e06bdc13b764:/# free -m
# 容器中内存使用情况
total used free shared buff/cache available
Mem: 3627 1211 1630 207 1239 2415
Swap: 2062 0 2062
# 虽然我们限制了容器的内存使用情况,但是查看到的信息依然是系统中内存的使用信息,并没有隔离开
2.2 解决Docker的默认隔离性
LXCFS 是一个为 LXC(Linux Containers)容器提供增强文件系统功能的工具。
主要功能:
- 资源可见性: LXCFS 可以使容器内的进程看到准确的 CPU、内存和磁盘 I/O 等资源使用信息。在没有 LXCFS 时,容器内看到的资源信息可能不准确,这会影响到在容器内运行的应用程序对资源的评估和 管理。
- 性能监控: 方便对容器内的资源使用情况进行监控和性能分析。通过提供准确的资源信息,管理员和开发 人员可以更好地了解容器化应用的性能瓶颈,并进行相应的优化。
bash
# 在rhe19中lxcfs是被包含在epel源中,我们可以直接下载安装包进行安装
[root@docker-node1 ~]# ls lxcfs
lxcfs-5.0.4-1.el9.x86_64.rpm lxc-libs-4.0.12-1.el9.x86_64.rpm lxc-templates-4.0.12-1.el9.x86_64.rpm
[root@docker-node1 ~]# dnf install lxcfs/*.rpm
# 运行lxcfs并解决容器隔离性
[root@docker-node1 ~]# lxcfs /var/lib/lxcfs &
[root@docker-node1 ~]# docker run -it -m 256m \
-v /var/lib/lxcfs/proc/cpuinfo:/proc/cpuinfo:rw \
-v /var/lib/lxcfs/proc/diskstats:/proc/diskstats:rw \
-v /var/lib/lxcfs/proc/meminfo:/proc/meminfo:rw \
-v /var/lib/lxcfs/proc/stat:/proc/stat:rw \
-v /var/lib/lxcfs/proc/swaps:/proc/swaps:rw \
-v /var/lib/lxcfs/proc/uptime:/proc/uptime:rw \
ubuntu
root@69ec0c67ff04:/# free -m
total used free shared buff/cache available
Mem: 256 1 254 0 0 254
Swap: 512 0 512
2.3 容器特权
在容器中默认情况下即使我是容器的超级用户也无法修改某些系统设定,比如网络
bash
[root@docker-node1 ~]# docker run --rm -it busybox
/ # whoami
root
/ # ip a
这是因为容器使用的很多资源都是和系统真实主机公用的,如果允许容器修改这些重要资源,系统的稳定性会变的非常差
但是由于某些需要求,容器需要控制一些默认控制不了的资源,如何解决此问题,这时我们就要设置容器特权
bash
[root@docker ~]# docker run --rm -it --privileged busybox
/ # id root
uid=0(root) gid=0(root) groups=0(root),10(wheel)
/ # ip a a 192.168.0.100/24 dev eth0
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
29: eth0@if30: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
inet 192.168.0.100/24 scope global eth0
valid_lft forever preferred_lft forever
/ # fdisk -l
Disk /dev/nvme0n1: 100 GB, 107374182400 bytes, 209715200 sectors
13003 cylinders, 256 heads, 63 sectors/track
Units: sectors of 1 * 512 = 512 bytes
Device Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type
/dev/nvme0n1p1 0,0,2 1023,255,63 1 209715199 209715199 99.9G ee EFI GPT
# 如果添加了--privileged 参数开启容器,容器获得权限近乎于宿主机的root用户
2.4 容器特权的白名单
--privileged=true 的权限非常大,接近于宿主机的权限,为了防止用户的滥用,需要增加限制,只提供给容器必须的权限。此时Docker 提供了权限白名单的机制,使用--cap-add添加必要的权限
capabilities手册地址:http://man7.org/linux/man-pages/man7/capabilities.7.html
bash
# 限制容器对网络有root权限
[root@docker-node1 ~]# docker run --rm -it --cap-add NET_ADMIN busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
72: eth0@if32: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
/ # ip a a 1.1.1.1/24 dev eth0 # 网络可以设定
/ # fdisk -l # 无法管理磁盘
/ #
八、 容器编排工具Docker Compose
1 Docker Compose 概述

Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。
其是官方的一个开源项目,托管到github上
网址:https://github.com/docker/compose
主要功能
- 定义服务: 使用 YAML 格式的配置文件来定义一组相关的容器服务。每个服务可以指定镜像、端口映射、环境变量、存储卷等参数。 例如,可以在配置文件中定义一个 Web 服务和一个数据库服务,以及它们之间的连接关系。
- 一键启动和停止: 通过一个简单的命令,可以启动或停止整个应用程序所包含的所有容器。这大大简化了多容器应用的部署和管理过程。 例如,使用 docker-compose up 命令可以启动配置文件中定义的所有服务,使用 docker-compose down 命令可以停止并删除这些服务。
- 服务编排: 可以定义容器之间的依赖关系,确保服务按照正确的顺序启动和停止。例如,可以指定数据库服务必须在 Web 服务之前启动。 支持网络配置,使不同服务的容器可以相互通信。可以定义一个自定义的网络,将所有相关的容器连接到这个网络上。
- 环境变量管理: 可以在配置文件中定义环境变量,并在容器启动时传递给容器。这使得在不同环境(如开发、测试和生产环境)中使用不同的配置变得更加容易。 例如,可以定义一个数据库连接字符串的环境变量,在不同环境中可以设置不同的值。
工作原理
- 读取配置文件: Docker Compose 读取 YAML 配置文件,解析其中定义的服务和参数。
- 创建容器: 根据配置文件中的定义,Docker Compose 调用 Docker 引擎创建相应的容器。它会下载所需 的镜像(如果本地没有),并设置容器的各种参数。
- 管理容器生命周期: Docker Compose 监控容器的状态,并在需要时启动、停止、重启容器。 它还可以处理容器的故障恢复,例如自动重启失败的容器。
Docker Compose 中的管理层
- 服务 (service) 一个应用的容器,实际上可以包括若干运行相同镜像的容器实例
- 项目 (project) 由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中 定义
- 容器(container)容器是服务的具体实例,每个服务可以有一个或多个容器。容器是基于服务定义 的镜像创建的运行实例
2 Docker Compose 的常用命令参数
bash
[root@docker-node1 ~]# vim docker/docker-compose.yml
services:
web:
image: nginx
ports:
- "80:80"
db:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: redhat
# 服务管理
启动
[root@docker-node1 ~]# cd docker/
[root@docker-node1 docker]# docker compose up -d
[+] Running 2/2
✔ Container docker-db-1 Running 0.0s
✔ Container docker-web-1 Started 0.4s
[root@docker-node1 ~]# docker compose -f docker/docker-compose.yml up -d
[+] Running 2/0
✔ Container docker-db-1 Running 0.0s
✔ Container docker-web-1 Running 0.0s
2.1 服务管理
- docker-compose up :
- 启动配置文件中定义的所有服务。
- 可以使用 -d 参数在后台启动服务。
- 可以使用-f 来指定yml文件 例如: docker-compose up -d 。
bash
[root@docker-node1 ~]# cd docker/
[root@docker-node1 docker]# docker compose up -d
[+] Running 2/2
✔ Container docker-db-1 Running 0.0s
✔ Container docker-web-1 Started 0.4s
[root@docker-node1 docker]# cd
[root@docker-node1 ~]#
[root@docker-node1 ~]# docker compose -f docker/docker-compose.yml up -d
[+] Running 2/0
✔ Container docker-db-1 Running 0.0s
✔ Container docker-web-1 Running 0.0s
- docker-compose down : 停止并删除配置文件中定义的所有服务以及相关的网络和存储卷。
bash
[root@docker-node1 ~]# docker compose -f docker/docker-compose.yml down
[+] Running 3/3
? Container docker-db-1 Removed 1.1s
? Container docker-web-1 Removed 0.1s
? Network docker_default Removed 0.1s
- docker-compose start : 启动已经存在的服务,但不会创建新的服务。
bash
[root@docker-node1 docker]# docker compose start
[+] Running 2/2
✔ Container docker-web-1 Started 0.4s
✔ Container docker-db-1 Started 0.3s
- docker-compose stop : 停止正在运行的服务
bash
[root@docker-node1 docker]# docker compose stop
[+] Stopping 2/2
✔ Container docker-web-1 Stopped 0.2s
✔ Container docker-db-1 Stopped 10.1s
- docker-compose restart : 重启服务。
bash
[root@docker-node1 docker]# docker compose restart
2.2 服务状态查看
- docker-compose ps :列出正在运行的服务以及它们的状态,包括容器 ID、名称、端口映射等信息
bash
[root@docker-node1 ~]# docker compose -f docker/docker-compose.yml ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
docker-db-1 mysql:5.7 "docker-entrypoint.s." db 3 minutes ago Up 3 minutes 3306/tcp, 33060/tcp
docker-web-1 nginx "/docker-entrypoint.¡." web 3 minutes ago Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp
- docker-compose logs : 查看服务的日志输出。可以指定服务名称来查看特定服务的日志。
bash
[root@docker-node1 docker]# docker compose logs db
2.3 构建和重新构建服务(了解)
-
docker-compose build : 构建配置文件中定义的服务的镜像。可以指定服务名称来只构建特定的服务。
-
docker-compose up --build : 启动服务并在启动前重新构建镜像。
2.4 其他操作
- docker-compose exec : 在正在运行的服务容器中执行命令。
bash
# test.yml
services:
test:
image: busybox
command: ["/bin/sh","-c","sleep 3000"]
restart: always
container_name: busybox1
[root@docker-node1 test]# docker compose -f test.yml up -d
[root@docker-node1 test]# docker compose -f test.yml exec test sh
/ #
- docker-compose pull : 拉取配置文件中定义的服务所使用的镜像。
bash
[root@docker-node1 test]# docker compose -f test.yml pull
[+] Pulling 2/2
✔ test Pulled
✔ ec562eabd705 Pull complete
- docker-compose config : 验证并查看解析后的 Compose 文件内容
bash
[root@docker-node1 test]# docker compose -f test.yml config
name: test
services:
test:
command:
- /bin/sh
- -c
- sleep 3000
container_name: busybox1
image: busybox
networks:
default: null
restart: always
networks:
default:
name: test_default
[root@docker-node1 test]# docker compose -f test.yml config -q
3 Docker Compose 的yml文件
Docker Compose 的 YAML 文件用于定义和配置多容器应用程序的各个服务。以下是一个基本的 Docker Compose YAML 文件结构及内容解释:
3.1 服务(services)
- 服务名称(service1_name/service2_name 等): 每个服务在配置文件中都有一个唯一的名称,用于在命令行和其他部分引用该服务。
bash
services:
web:
# 服务1的配置
mysql:
# 服务2的配置
- 镜像(image): 指定服务所使用的 Docker 镜像名称和标签。例如, image: nginx:latest 表示使用 nginx 镜像的最新版本
bash
services:
web:
images:nginx
mysql:
images:mysql:5.7
- 端口映射(ports): 将容器内部的端口映射到主机的端口,以便外部可以访问容器内的服务。例如, - "8080:80" 表示将主机的 8080 端口映射到容器内部的 80 端口。
bash
services:
web:
image: superhowe/mario
container_name: game # 指定容器名称
restart: always # docekr容器自动启动
expose:
- 1234 # 指定容器暴露那些端口,些端口仅对链接的服务可见,不会映射到主机的端口
ports:
- "80:8080"
- 环境变量(environment): 为容器设置环境变量,可以在容器内部的应用程序中使用。例如, VAR1: value1 设置环境变 量 VAR1 的值为 value1
bash
services:
web:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: redhat
- 存储卷(volumes): 将主机上的目录或文件挂载到容器中,以实现数据持久化或共享。例如, - /host/data:/container/data 将主机上的 /host/data 目录挂载到容器内的 /container/data 路径。
bash
services:
web:
image: busybox
command: ["/bin/sh","-c","sleep 100000"]
container_name: busybox1
volumes:
- /etc/passwd:/tmp/passwd:ro # 只读挂在本地文件到指定位置
- 网络(networks): 将服务连接到特定的网络,以便不同服务的容器可以相互通信
bash
services:
web:
image: nginx
container_name: webserver
network_mode: bridge # 使用本机自带bridge网络
services:
test:
image: busybox
container_name: webserver
command: ["/bin/sh","-c","sleep10000000"]
networks:
- mynet1
- mynet2
networks:
mynet1:
driver: bridge
mynet2:
driver: bridge
bash
services:
web:
image: busybox
container_name: busybox
#network_mode: mynet2
command: ["/bin/sh","-c","sleep10000000"]
3.2 网络(networks)
定义 Docker Compose 应用程序中使用的网络。可以自定义网络名称和驱动程序等属性。 默认情况下docker compose 在执行时会自动建立网络
bash
services:
test1:
image: busybox:latest
command: ["/bin/sh","-c","sleep 10000"]
network_mode: default
container_name: test
test2:
image: busybox:latest
command: ["/bin/sh","-c","sleep 100000"]
container_name: test2
networks:
- mynet1
- mynet2
networks:
default:
external: true
name: bridge
mynet1:
driver: brige
mynet2:
ipam:
driver: default
config:
- subnet: 172.26.0.0/24
gateway: 172.26.0.2
[root@docker-node1 test]# docker compose up -d
3.3 存储卷(volumes)
定义 Docker Compose 应用程序中使用的存储卷。可以自定义卷名称和存储位置等属性。
bash
services:
test1:
image: busybox:latest
command: ["/bin/sh","-c","sleep 10000"]
network_mode: default
container_name: test
test2:
image: busybox:latest
command: ["/bin/sh","-c","sleep 100000"]
container_name: test2
networks:
- mynet1
- mynet2
volumes:
- data:/data # 挂在data卷
networks:
default:
external: true
name: bridge
mynet1:
driver: bridge
mynet2:
ipam:
driver: default
config:
- subnet: 172.26.0.0/24
gateway: 172.26.0.2
volumes:
data:
name: howe # 指定建立卷的名字
# 检测语法错误
docker compose config
docker compose config -q # 不打印错误
4 企业示例
利用容器编排完成haproxy和nginx负载均衡架构实施
bash
[root@docker-node1 ~]# yum install haproxy -y --downloadonly --downloaddir=/mnt
[root@docker-node1 test]# cd /mnt/
[root@docker-node1 mnt]# ls
haproxy-2.4.22-3.el9_3.x86_64.rpm hgfs
[root@docker-node1 mnt]# rpm2cpio haproxy-2.4.22-3.el9_3.x86_64.rpm | cpio -id
13488 blocks
[root@docker-node1 mnt]# ls
etc haproxy-2.4.22-3.el9_3.x86_64.rpm hgfs usr var
[root@docker-node1 mnt]# cd etc/
[root@docker-node1 etc]# ls
haproxy logrotate.d sysconfig
[root@docker-node1 etc]# cd haproxy/
[root@docker-node1 haproxy]# ls
conf.d haproxy.cfg
[root@docker-node1 haproxy]# mkdir /var//lib/docker/volumes/conf
[root@docker-node1 haproxy]# cp haproxy.cfg /var/lib/docker/volumes/conf/
[root@docker-node1 haproxy]# cd /var/lib/docker/volumes/conf/
[root@docker-node1 conf]# ls
haproxy.cfg
[root@docker-node1 conf]# vim haproxy.cfg
...
listen webcluter
bind *:80
balance roundrobin
server web1 webserver1:80 check inter 3 fall 3 rise 5
server web2 webserver2:80 check inter 3 fall 3 rise 5
...
[root@docker-node1 ~]# cd test/
[root@docker-node1 test]# ls
docker-compose.yml
[root@docker-node1 test]# vim docker-compose.yml
[root@docker-node1 test]# cat docker-compose.yml
services:
web1:
image: nginx:latest
container_name: webserver1
restart: always
expose:
- 80
volumes:
- data_web1:/usr/share/nginx/html
networks:
- internel
web2:
image: nginx:latest
container_name: webserver2
restart: always
expose:
- 80
volumes:
- data_web2:/usr/share/nginx/html
networks:
- internel
haproxy:
image: haproxy:2.3
restart: always
container_name: haproxy
ports:
- "80:80"
volumes:
- /var/lib/docker/volumes/conf/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
networks:
- internel
- extrnal
networks:
internel:
driver: bridge
extrnal:
driver: bridge
volumes:
data_web1:
name: data_web1
data_web2:
name: data_web2
[root@docker-node1 test]# docker compose -f docker-compose.yml up -d
[+] Running 5/5
✔ Network test_mynet1 Created
0.1s
✔ Network test_mynet2 Created
0.1s
✔ Container haproxy Started
0.8s
✔ Container web1 Started
0.7s
✔ Container web2 Started
0.6s
[root@docker-node1 test]# docker compose -f haproxy.yml up -d
[+] Running 7/7
✔ Network test_internel Created 0.1s
✔ Network test_extrnal Created 0.1s
✔ Volume "data_web2" Created 0.0s
✔ Volume "data_web1" Created 0.0s
✔ Container haproxy Started 1.2s
✔ Container webserver1 Started 1.2s
✔ Container webserver2 Started 1.1s
[root@docker-node1 test]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7158668dd70f nginx:latest "/docker-entrypoint...." 7 seconds ago Up 6 seconds 80/tcp webserver2
8aa9a99597df haproxy:2.3 "docker-entrypoint.s..." 7 seconds ago Up 6 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp haproxy
d5f6b242b8e1 nginx:latest "/docker-entrypoint...." 7 seconds ago Up 6 seconds 80/tcp webserver1
# 写入内容并测试:
[root@docker-node1 test]# echo webserver1 > /var/lib/docker/volumes/data_web1/_data/index.html
[root@docker-node2 test]# echo webserver2 > /var/lib/docker/volumes/data_web2/_data/index.html
[root@docker-node1 test]# curl 172.25.250.200
webserver2
[root@docker-node1 test]# curl 172.25.250.200
webserver1
[root@docker-node1 test]# curl 172.25.250.200
webserver2
[root@docker-node1 test]# curl 172.25.250.200
webserver1