多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
:-: ![ChMkJlXRnDCIH09oAACwpjAygMYAAAMuAH-4nMAALC-242](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/ChMkJlXRnDCIH09oAACwpjAygMYAAAMuAH-4nMAALC-242.png) [toc] ## Docker的起源 2010年,美国几个年轻人成立dotcloud,基于linux的LXC容器技术 后期更名为Docker,为了更有影响力,2013决定开源。 ![ChMkJ1XRnC2IQ7KBAAJJ3hbQKYoAAAMuAGX_dUAAkn2298](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/ChMkJ1XRnC2IQ7KBAAJJ3hbQKYoAAAMuAGX_dUAAkn2298.png) ![ChMkJ1XRnOaISYkOAAHO_HkoN04AAAMugH1QcAAAc8U241](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/ChMkJ1XRnOaISYkOAAHO_HkoN04AAAMugH1QcAAAc8U241.png) ## Docker三大核心组件 - 镜像(images):类似虚拟机镜像 - 仓库(Repository):集中存放镜像的地方。每个服务器上可以有多个仓库。 - 容器(containers):镜像的一个运行实例,类似linux系统环境,运行和隔离应用 ## Docker核心技术 ### NameSpace 简单来说,**Namespace 可以为容器提供系统资源隔离能力**。 当然,这样讲过于笼统,我们来举个例子:假如一个容器中的进程需要使用 root 权限,出于安全考虑,我们不可能把宿主机的 root 权限给他。但是通过 Namespace 机制,我们可以隔离宿主机与容器的真实用户资源,谎称一个普通用户就是 root,**骗**过这个程序。从这个角度看,**Namespace 就是内核对进程说谎的机制**,目前(Linux最新的稳定版本为5.6),内核可以说的**谎话**有 8 种: <img src="https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220313132517638.png" alt="image-20220313132517638" style="zoom:50%;" /> 感受 Namespace <img src="https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220313133410216.png" alt="image-20220313133410216" style="zoom:50%;" /> ### CGroup Namespace 只能做到系统资源维度的隔离,无法做到硬件资源的控制。我们需要使用一种机制 **Cgroup**,指定容器应用最大占用多少资源。 Linux cgroups 的全称是 Linux Control Groups,它是 Linux 内核的特性,主要作用是限制、记录和隔离进程组(process groups)使用的物理资源(CPU、Memory、IO 等)。 因此 CGroup 需要考虑如何抽象这两种概念:**进程和资源,同时如何组织自己的结构**。CGroup 机制中有以下几个基本概念: - **task**:任务,对应于系统中运行的一个实体,下文统称进程; - **subsystem**:子系统,具体的资源控制器(resource class 或者 resource controller),控制某个特定的资源使用; - **cgroup**:控制组,一组任务和子系统的关联关系,表示对这些任务进行怎样的资源管理策略; - **hierarchy**:层级树,由一系列 CGroup 组成的树形结构。每个节点都是一个 CGroup ,CGroup 可以有多个子节点,子节点默认会继承父节点的属性。系统中可以有多个 hierarchy 在 Linux 环境中,我们可以执行 `ls -al /sys/fs/cgroup/` 查看当前系统的 `Cgroup: <img src="https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220313135429910.png" alt="image-20220313135429910" style="zoom:50%;" /> 我们看到目录中有若干个子目录,除了 systemd 目录,其他的一个子目录对应一个子系统,子系统功能如下所示。 <img src="https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220313135521688.png" alt="image-20220313135521688" style="zoom:50%;" /> **容器的本质就是一个特殊的进程** ## Docker的安装 参考文档: https://mirrors.tencent.com/help/docker-ce.html https://developer.aliyun.com/article/110806 https://docs.docker.com/engine/install/centos/ (官方文档) ### 1、卸载老版本 前提需要卸载系统内老版本Docker,如果是新系统可跳过此步骤 ```bash yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine ``` ### 2、安装依赖 ```bash yum install -y yum-utils \ device-mapper-persistent-data \ lvm2 ``` ### 3、添加docker镜像仓库 ```bash wget -O /etc/yum.repos.d/docker-ce.repo https://download.docker.com/linux/centos/docker-ce.repo ``` ### 4、替换仓库地址 > 阿里云仓库地址: ```bash yum-config-manager \ --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo ``` > 腾讯云仓库地址: ```bash sed -i 's+download.docker.com+mirrors.cloud.tencent.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo ``` ### 5、更新yum包索引 ```bash yum makecache fast ``` ### 6、安装docker CE ```bash yum install docker-ce docker-ce-cli containerd.io -y ``` 选择版本安装: ```bash yum list docker-ce.x86_64 --showduplicates | sort -r ``` #从高到低列出Docker-ce的版本 yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io 例如: ```bash yum install docker-ce-18.09.9 docker-ce-cli-18.09.9 containerd.io ``` 使用官方安装脚本自动安装: ```bash curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun ``` ### 7、启动docker ```bash systemctl start docker ``` #### 7.1 验证 通过运行hello-world 映像来验证是否正确安装了Docker Engine 。 ```bash docker run hello-world ``` ### 8、卸载 *列出docker包的具体名字 ```bash yum list installed | grep docker yum remove docker-ce docker-ce-cli containerd.io ``` 以上命令只会删除docker运行环境,并不会删除镜像,容器,卷文件,以及用户创建的配置文件。 删除资源目录: ```bash rm -rf /var/lib/docker ``` ### 9、镜像加速 https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors?accounttraceid=4f5db75ebc8b410fb30d4bcbac0f5a5cvdpx 使用私人镜像加速器地址,如使用阿里云的镜像加速器:登录阿里云->产品->搜索"容器镜像服务"->镜像库->镜像加速器 Docker 使用 /etc/docker/daemon.json ```bash vim /etc/docker/daemon.json { "registry-mirrors": ["https://9s0l9akh.mirror.aliyuncs.com"] } systemctl daemon-reload systemctl restart docker ``` ==**问题:**== yum install docker-ce docker-ce-cli containerd.io -y 报错如图: ![](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/warning-1.png) ```bash #解决 /etc/yum.repos.d/docker-ce.repo [centos-extras] name=Centos extras - $basearch baseurl=http://mirror.centos.org/centos/7/extras/x86_64 enabled=1 gpgcheck=1 gpgkey=http://centos.org/keys/RPM-GPG-KEY-CentOS-7 yum -y install slirp4netns fuse-overlayfs container-selinux yum -y install docker-ce docker-ce-cli containerd.io ``` ## Docker的常用命令 ### 帮助命令 ```bash docker version #显示docker的版本信息 docker info #显示docker的详细信息 docker 命令 --help #帮助命令 ``` 查看docker日志 ```bash cat /var/log/docker ``` ### 镜像命令 ![image-20220312111656363](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220312111656363.png) ```bash docker images #查看当前所有镜像 #解释 REPOSITORY 镜像的仓库源 TAG 镜像标签 IMAGE ID 镜像的ID CREATED 镜像的创建时间 SIZE 镜像的大小 -a 列出本地所有镜像,含中间映像层 -q 只显示镜像ID 所有镜像存储在/var/lib/docker ``` 搜索镜像 ```bash docker search tomcat #去镜像仓库搜索镜像 docker search nginx docker search mysql --filter=stars=3000 ``` 下载镜像 ```bash docker pull centos #下载镜像[:tag] [root@bothtech ~]# docker pull mysql Using default tag: latest #如果不写tag默认为latest latest: Pulling from library/mysql 72a69066d2fe: Pull complete #分层下载、image联合文件系统 93619dbc5b36: Pull complete 99da31dd6142: Pull complete 626033c43d70: Pull complete 37d5d7efb64e: Pull complete ac563158d721: Pull complete d2ba16033dad: Pull complete 688ba7d5c01a: Pull complete 00e060b6d11d: Pull complete 1c04857f594f: Pull complete 4d7cfa90e6ea: Pull complete e0431212d27d: Pull complete Digest: sha256:e9027fe4d91c0153429607251656806cc784e914937271037f7738bd5b8e7709 #签名 Status: Downloaded newer image for mysql:latest docker.io/library/mysql:latest #真实地址 ``` 指定版本 ```bash [root@bothtech ~]# docker pull mysql:5.7.37 5.7.37: Pulling from library/mysql 15115158dd02: Pull complete d733f6778b18: Pull complete 1cc7a6c74a04: Pull complete c4364028a805: Pull complete 82887163f0f6: Pull complete 28abcb7f57e0: Pull complete 46d27a431703: Pull complete 146a7517cdca: Pull complete ac645a526e45: Pull complete a292dcc315cc: Pull complete ff70b7ef8a8b: Pull complete Digest: sha256:66d52e6baa8093820c09fec56992a5ee734f17e9fad8ef5ffc31597b231bd048 Status: Downloaded newer image for mysql:5.7.37 docker.io/library/mysql:5.7.37 ``` 删除镜像 通过id和名字都可以进行删除 删除一个或者多个: ```bash docker rmi -f id1 id2 #支持删除多个,使用空格分割 ``` 删除全部镜像: ```bash docker rmi -f (docker images -qa) ``` ### 容器命令 新建容器并启动 ```bash docker run [可选参数] image --name=‘name’ 容器名字,区分容器 -d 后台方式运行 -it 使用交互方法,进入容器查看内容 -p 指定容器端口 #-p ip:宿主机端口:容器端口 #-p 宿主机端口:容器端口 (常用) #-p 容器端口 -P 随机指定端口 -v <空/宿主机目录路径/文件>:<容器目录路径或文件> # 三种挂载: 匿名挂载、具名挂载、指定路径挂载 --restart=always 容器随docker启动而运行 (如果忘记添加,可以后续通过“docker container update --restart=always 容器名” 来更新参数) ``` 运行并进入容器centos ```bash [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker run -it centos /bin/bash [root@bd1b8900c547 /]# ls bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var ``` 退出容器命令 ```bash #exit 停止并退出容器(后台方式运行则仅退出) #Ctrl+P+Q 不停止容器退出 ``` 列出运行过的容器命令 ```bash #docker ps # 列出当前正在运行的容器 -a # 列出所有容器的运行记录 -n=? # 显示最近创建的n个容器 -q # 只显示容器的编号 docker ps -qaf 'name=cpnginx' #从容器名称获取容器ID [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES bca129320bb5 centos "/bin/bash" 4 minutes ago Exited (0) 3 minutes ago optimistic_shtern bd1b8900c547 centos "/bin/bash" 6 minutes ago Exited (0) 5 minutes ago cool_tesla cf6adbf1b506 bf756fb1ae65 "/hello" 5 hours ago Exited (0) 5 hours ago optimistic_darwin ``` 删除容器命令 ```bash docker rm 容器id #删除指定的容器,不能删除正在运行的容器,强制删除使用 rm -f docker rm -f (docker ps -aq) #删除所有的容器 docker ps -a -q|xargs docker rm #删除所有的容器 ``` 启动和停止容器命令 ```bash docker start 容器id #启动容器,关闭的容器重新启动 docker restart 容器id #重启容器 docker stop 容器id #停止当前运行的容器 docker kill 容器id #强制停止当前容器 ``` ### 其他常用命令 查看容器ip ```bash [root@ecs-b8ca-0817460 ~]# docker inspect --format='{{.NetworkSettings.IPAddress}}' php-web 172.17.0.3 [root@ecs-b8ca-0817460 ~]# docker inspect --format='{{.NetworkSettings.IPAddress}}' nginx-web 172.17.0.2 ``` 查看容器的端口映射: ```bash [root@bothtech ~]# docker port mysql-web ``` 查看容器的目录映射: ```bash [root@bothtech ~]# docker inspect mysql-web ``` 日志查看 ```bash [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker logs --help Usage: docker logs [OPTIONS] CONTAINER Fetch the logs of a container Options: --details Show extra details provided to logs -f, --follow Follow log output --since string Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes) -n, --tail string Number of lines to show from the end of the logs (default "all") -t, --timestamps Show timestamps --until string Show logs before a timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes) 常用: docker logs -tf 容器id docker logs --tail number 容器id #num为要显示的日志条数 #docker容器后台运行,必须要有一个前台的进程,否则会自动停止 #编写shell脚本循环执行,使得centos容器保持运行状态 [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker run -d centos /bin/sh -c "while true;do echo hi;sleep 5;done" c703b5b1911ff84d584390263a35707b6024816e1f46542b61918a6327a570dc [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c703b5b1911f centos "/bin/sh -c 'while t…" 13 seconds ago Up 10 seconds pedantic_banach [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker logs -tf --tail 10 c703b5b1911f 2020-12-27T03:34:07.255599560Z hi 2020-12-27T03:34:12.257641517Z hi 2020-12-27T03:34:17.259706294Z hi 2020-12-27T03:34:22.261693707Z hi 2020-12-27T03:34:27.262609289Z hi 2020-12-27T03:34:32.267862677Z hi 2020-12-27T03:34:37.270382873Z hi 2020-12-27T03:34:42.272414182Z hi 2020-12-27T03:34:47.274823243Z hi 2020-12-27T03:34:52.277419274Z hi ``` 查看容器中进程信息 ```bash [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker top c703b5b1911f UID PID PPID C STIME TTY TIME CMD root 11156 11135 0 11:31 ? 00:00:00 /bin/sh -c while true;do echo hi;sleep 5;done root 11886 11156 0 11:43 ? 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 5 ``` 查看容器的元数据 ```bash [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker inspect 容器id ``` 进入正在运行中的容器 因为通常我们的容器都是使用后台方式来运行的,有时需要进入容器修改配置 方式一: ```bash [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker exec -it c703b5b1911f /bin/bash [root@c703b5b1911f /]# ls bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var [root@c703b5b1911f /]# ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 03:31 ? 00:00:00 /bin/sh -c while true;do echo hi;sleep 5;done root 279 0 0 03:54 pts/0 00:00:00 /bin/bash root 315 1 0 03:56 ? 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 5 root 316 279 0 03:56 pts/0 00:00:00 ps -ef ``` 方式二: ```bash [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker attach c703b5b1911f ``` #docker exec 进入容器后开启一个新的终端,可以在里面操作 #docker attach 进入容器正在执行的终端,不会启动新的进程 拷贝操作命令 ```bash #拷贝容器的文件到主机中 docker cp 容器id:容器内路径 目的主机路径 #拷贝宿主机的文件到容器中 docker cp 目的主机路径 容器id:容器内路径 ``` ```bash [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker exec -it c703b5b1911f /bin/bash [root@c703b5b1911f /]# cd home [root@c703b5b1911f home]# ls #touch 新建文件 [root@c703b5b1911f home]# touch test.java [root@c703b5b1911f home]# ls test.java [root@c703b5b1911f home]# exit exit [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c703b5b1911f centos "/bin/sh -c 'while t…" 35 minutes ago Up 35 minutes pedantic_banach [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker cp c703b5b1911f:/home/test.java /home [root@iZwz99sm8v95sckz8bd2c4Z ~]# ls /home hai pan test.java ``` <img src="https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/src=http___img-blog.csdnimg.cn_img_convert_e86543bb903f3f989485e4cb5e3e194e.png&refer=http___img-blog.csdnimg.webp" alt="src=http___img-blog.csdnimg.cn_img_convert_e86543bb903f3f989485e4cb5e3e194e.png&refer=http___img-blog.csdnimg" style="zoom:70%;" /> ### docker容器中安装vim 、telnet、ifconfig, ping命令 ```java apt-get update apt-get install vim 安装vim apt-get install telnet 安装telnet apt-get install net-tools 安装ifconfig apt-get install iputils-ping 安装ping apt-get install iproute2 安装ip addr ``` ## 图形化管理工具 ### Portaniner安装 Portaniner是Docker的图形化管理工具,类似的工具还有Rancher(CI/CD再用) 下载运行Portaniner镜像并运行,设置宿主机映射端口为8088,如果为云服务器需要开放端口进行访问 ```java [root@localhost conf]# docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer Unable to find image 'portainer/portainer:latest' locally latest: Pulling from portainer/portainer 94cfa856b2b1: Pull complete 49d59ee0881a: Pull complete a2300fd28637: Pull complete Digest: sha256:fb45b43738646048a0a0cc74fcee2865b69efde857e710126084ee5de9be0f3f Status: Downloaded newer image for portainer/portainer:latest 8c525a0137be22965bd1e3944da622a2c4248f8ad20883f4b3ea4f8a6b11e163 [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 7789d4505a00 portainer/portainer "/portainer" 6 seconds ago Up 5 seconds 0.0.0.0:8088->9000/tcp quirky_sinoussi ``` ### kitematic安装 https://github.com/docker/kitematic/releases Kitematic 目前在 [Github](https://github.com/docker/kitematic) 上开源,而它也早在 2015 年就已经被 Docker 收购,现在集成在docker desktop Kitematic 完全自动化了 Docker 安装和设置过程,并提供了一个直观的图形用户接口(GUI)来运行 Docker。 通过 GUI 你可以非常容易的创建、运行和管理你的容器,不需要使用命令行或者是在 Docker CLI 和 GUI之间来回切换; 同时也可以方便的修改环境变量、查看日志以及配置数据卷等。 https://github.com/docker/kitematic/releases/download/v0.17.13/Kitematic-0.17.13-Mac.zip 安装后打开,输入docker的用户名密码登录或者点击右下角的 Skip For Now 忽略登录 卸载: **Mac** - Remove Kitematic.app - Remove any unwanted Virtual Machines in VirtualBox ```bash # remove app data rm -rf ~/Library/Application\ Support/Kitematic ``` **Windows** Open `Programs and Features` from `Control Panel` - Uninstall Kitematic - Uninstall Oracle VM VirtualBox ## Docker镜像详解 ### 1、什么是镜像 镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需要的所有内容,包括代码,运行时(一个程序在运行或者在被执行的依赖)、库,环境变量和配置文件。 ### 2、UnionFS(联合文件系统) **UnionFS(联合文件系统)**:是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。 Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像 **特性:**一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录 ### 3、bootfs与rootfs **bootfs(boot fle system)**:主要包含bootloader和kernel, bootloader主要是引导加载kernel.Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们典型的LinuxUnix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。 **rootfs (root fle system)**,在boots之上。包含的就是典型Linux系统中的ldev,lproc,/bin, letc等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。 <img src="https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/466dae85e94640f293956b9d6f386792.png" alt="466dae85e94640f293956b9d6f386792" style="zoom:70%;" /> ### 4、Docker镜像加载原理 <img src="https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/WechatIMG162.png" alt="WechatIMG162" style="zoom:50%;" /> ### 5、Docker镜像分层理解 Docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统是UnionFS联合文件系统。它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。 ![20210718123512798](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/20210718123512798.png) ```bash [root@bothtech 1]# docker image inspect nginx:1.20.2 [ { "Id": "sha256:50fe74b50e0d0258922495297efbb9ebc3cbd5742103df1ca54dc21c07d24575", "RepoTags": [ "nginx:1.20.2" ], "RepoDigests": [ "nginx@sha256:03f3cb0afb7bd5c76e01bfec0ce08803c495348dccce37bcb82c347b4853c00b" ], "Parent": "", "Comment": "", "Created": "2021-12-21T03:02:00.73193624Z", "Container": "99fb95d2e2aeeee02f02da7b80d3e5f9b17b39dd77396c0ada20cf398413959a", "ContainerConfig": { "Hostname": "99fb95d2e2ae", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "ExposedPorts": { "80/tcp": {} }, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "NGINX_VERSION=1.20.2", "NJS_VERSION=0.7.0", "PKG_RELEASE=1~bullseye" ], "Cmd": [ "/bin/sh", "-c", "#(nop) ", "CMD [\"nginx\" \"-g\" \"daemon off;\"]" ], "Image": "sha256:df26cc3cb2d247e6a3fde577d02fe0c79843535f5824deb0d460ae15b53e730b", "Volumes": null, "WorkingDir": "", "Entrypoint": [ "/docker-entrypoint.sh" ], "OnBuild": null, "Labels": { "maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>" }, "StopSignal": "SIGQUIT" }, "DockerVersion": "20.10.7", "Author": "", "Config": { "Hostname": "", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "ExposedPorts": { "80/tcp": {} }, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "NGINX_VERSION=1.20.2", "NJS_VERSION=0.7.0", "PKG_RELEASE=1~bullseye" ], "Cmd": [ "nginx", "-g", "daemon off;" ], "Image": "sha256:df26cc3cb2d247e6a3fde577d02fe0c79843535f5824deb0d460ae15b53e730b", "Volumes": null, "WorkingDir": "", "Entrypoint": [ "/docker-entrypoint.sh" ], "OnBuild": null, "Labels": { "maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>" }, "StopSignal": "SIGQUIT" }, "Architecture": "amd64", "Os": "linux", "Size": 141447928, "VirtualSize": 141447928, "GraphDriver": { "Data": { "LowerDir": "/var/lib/docker/overlay2/3c9bf8c2b9600c9a1db12cfe9a5cc164c31c68ea814e5052b2c44bdf22de7a46/diff:/var/lib/docker/overlay2/23c279c89aac85ce2f68743c76967f74bb6f29cbe40bcf82b09e96709b30f181/diff:/var/lib/docker/overlay2/c6e434601be4f9e875b87307139101e3a60927e1aff85f556c6a57f9e1ad474e/diff:/var/lib/docker/overlay2/bd3aadbe838682052d7e9d743869362018ce9e1fe2dfd480fcc377a8d6213272/diff:/var/lib/docker/overlay2/47e21691ccef3d7aceae068e199220fb5b30323a412f8366374dc32088e92a91/diff", "MergedDir": "/var/lib/docker/overlay2/59fb3ffeb5199c79987e122748d9066dda0a9dfdc93434f70507b416c6bc6dcb/merged", "UpperDir": "/var/lib/docker/overlay2/59fb3ffeb5199c79987e122748d9066dda0a9dfdc93434f70507b416c6bc6dcb/diff", "WorkDir": "/var/lib/docker/overlay2/59fb3ffeb5199c79987e122748d9066dda0a9dfdc93434f70507b416c6bc6dcb/work" }, "Name": "overlay2" }, "RootFS": { "Type": "layers", "Layers": [ "sha256:2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f", "sha256:9ccbab2746b88b2992bfff96811aad769d8dc6f272077d19d7a831de859d9dc7", "sha256:108a6d6c3e60d60490f41cd8c1866a9fda99f79448742970b4b8aa7f7e1cd46f", "sha256:35437a3771fcd62ec7661c439d3df698699fd1229d220fbec8f330f2df5616e1", "sha256:4e498ce5ae6a4ff9a644958303b5cc140a99831025883173ebb3ae7d00b98500", "sha256:c75c795b7d446c20af2827f10f55804b93fba2a319444e297a2951261ae14fc1" ] }, "Metadata": { "LastTagTime": "0001-01-01T00:00:00Z" } } ] ``` 这里显示分层信息: <img src="https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220313143932574.png" alt="image-20220313143932574" style="zoom:50%;" /> ![20210718123636415](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/20210718123636415.png) ![2021071812372978](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/2021071812372978.png) ![2021071812374035](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/2021071812374035.png) - Docker镜像都是只读的 - 当容器启动时,一个新的可写层被加载到镜像的顶部 - 这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层” - ### 6、提交镜像 ```bash 使用docker commit 命令提交容器成为一个新的版本 docker commit -m=“提交的描述信息” -a="作者" 容器id 目标镜像名:[TAG] ``` 由于默认的Tomcat镜像的webapps文件夹中没有任何内容,需要从webapps.dist中拷贝文件到webapps文件夹。下面自行制作镜像:就是从webapps.dist中拷贝文件到webapps文件夹下,并提交该镜像作为一个新的镜像。使得该镜像默认的webapps文件夹下就有文件。具体命令如下: ```bash #1.复制文件夹 [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker run -it tomcat /bin/bash root@2a3bf3eaa2e4:/usr/local/tomcat# cd webapps root@2a3bf3eaa2e4:/usr/local/tomcat/webapps# ls root@2a3bf3eaa2e4:/usr/local/tomcat/webapps# cd ../ root@2a3bf3eaa2e4:/usr/local/tomcat# cp -r webapps.dist/* webapps root@2a3bf3eaa2e4:/usr/local/tomcat# cd webapps root@2a3bf3eaa2e4:/usr/local/tomcat/webapps# ls ROOT docs examples host-manager manager [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2a3bf3eaa2e4 tomcat "/bin/bash" 4 minutes ago Up 4 minutes 8080/tcp competent_torvalds 7789d4505a00 portainer/portainer "/portainer" 24 hours ago Up 24 hours 0.0.0.0:8088->9000/tcp quirky_sinoussi [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker exec -it 2a3bf3eaa2e4 /bin/bash root@2a3bf3eaa2e4:/usr/local/tomcat# cd webapps root@2a3bf3eaa2e4:/usr/local/tomcat/webapps# ls ROOT docs examples host-manager manager [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2a3bf3eaa2e4 tomcat "/bin/bash" 8 minutes ago Up 8 minutes 8080/tcp competent_torvalds 7789d4505a00 portainer/portainer "/portainer" 24 hours ago Up 24 hours 0.0.0.0:8088->9000/tcp quirky_sinoussi #2.提交镜像作为一个新的镜像 [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker commit -m="add webapps" -a="Ethan" 2a3bf3eaa2e4 mytomcat:1.0 sha256:f189aac861de51087af5bc88a5f1de02d9574e7ee2d163c647dd7503a2d3982b [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE mytomcat 1.0 f189aac861de 7 seconds ago 653MB mysql 5.7 f07dfa83b528 6 days ago 448MB tomcat latest feba8d001e3f 10 days ago 649MB nginx latest ae2feff98a0c 12 days ago 133MB centos latest 300e315adb2f 2 weeks ago 209MB portainer/portainer latest 62771b0b9b09 5 months ago 79.1MB elasticsearch 7.6.2 f29a1ee41030 9 months ago 791MB #3.运行容器 [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker run -it mytomcat:1.0 /bin/bash root@1645774d4605:/usr/local/tomcat# cd webapps root@1645774d4605:/usr/local/tomcat/webapps# ls ROOT docs examples host-manager manager ``` ## 创建Docker容器 ### 1、创建nginx容器 ```bash mkdir -p /data/nginx/{html,conf/conf.d,logs} docker pull nginx:1.20.2 docker run -d -p 8081:80 --name cpnginx nginx:1.20.2 docker cp cpnginx:/etc/nginx/nginx.conf /data/nginx/conf/ docker stop `docker ps -qaf 'name=cpnginx'` docker rm `docker ps -qaf 'name=cpnginx'` docker run -d -p 8081:80 --name mynginx -v /data/nginx/html:/usr/share/nginx/html -v /data/nginx/conf/nginx.conf:/etc/nginx/nginx.conf -v /data/nginx/conf/conf.d:/etc/nginx/conf.d -v /data/nginx/logs:/var/log/nginx nginx:1.20.2 ``` ### 2、创建tomcat容器 ```bash [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker pull tomcat Using default tag: latest latest: Pulling from library/tomcat 6c33745f49b4: Pull complete ef072fc32a84: Pull complete c0afb8e68e0b: Pull complete d599c07d28e6: Pull complete e8a829023b97: Pull complete d04be46a31d1: Pull complete db6007c69c35: Pull complete e4ad4c894bce: Pull complete 248895fda357: Pull complete 277059b4cba2: Pull complete Digest: sha256:57dae7dfb9b62a413cde65334c8a18893795cac70afc3be589c8336d8244655d Status: Downloaded newer image for tomcat:latest docker.io/library/tomcat:latest [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker run -d -p 3335:8080 --name tomcat01 tomcat 7136295a6082cb0f805b025a1471bde02ead4864be3e2c9dcd337b1dde0a3113 ``` ### 3、创建mysql容器 ```bash mkdir -p /data/mysql/{data,conf,logs} docker run -d --name mysql5737 -p 3306:3306 -v /data/mysql/conf:/etc/mysql/conf.d -v /data/mysql/data:/var/lib/mysql -v /data/mysql/logs:/var/log/mysql -e MYSQL_ROOT_PASSWORD=johnny1314 mysql:5.7.37 “数据库未初始化,密码没设置。你需要设置MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD三个中的任意一项” 其中 MYSQL_ROOT_PASSWORD即root账户的密码。 MYSQL_ALLOW_EMPTY_PASSWORD即允许密码为空。 MYSQL_RANDOM_ROOT_PASSWORD随机一个root账户密码。 ``` ### 4、创建php容器 在使用nginx的时候必须先启动php容器,启动php容器后可以把php容器网络加入到nginx中,启动php时候可以不做9000端口映射 ```bash docker run -d --name php-web -p 9000:9000 -v /data/nginx/html:/www php:7.4.28 ``` php安装扩展: ```bash docker-php-ext-install 扩展名称 docker-php-ext-install pdo_mysql ``` 时区: ```bash cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ``` ### 5、创建ElasticSearch容器 添加 ’-e ES_JAVA_OPTS="-Xms128m -Xmx512m" ‘ 配置ElasticSearch的虚拟机占用的内存大小。 docker stats 查看资源占用情况 ```bash docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms128m -Xmx512m" elasticsearch:7.6.2 [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker run -d --name elasticsearch01 -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms128m -Xmx512m" elasticsearch:7.6.2 3b8cd4991814896c523ee67b84ce198e32bd82b1a62d512b198138a58ca946f1 [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3b8cd4991814 elasticsearch:7.6.2 "/usr/local/bin/dock…" 10 seconds ago Up 6 seconds 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp elasticsearch01 [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker stats ``` ## 容器数据卷 ### 1、数据卷介绍 Docker将运用与运行的环境打包形成容器运行, Docker容器产生的数据,如果不通过docker commit生成新的镜像,使得数据做为镜像的一部分保存下来, 那么当容器删除后,数据自然也就没有了。 为了能保存数据在Docker中我们使用卷。| 卷就是目录或文件,存在于一个或多个容器中,由Docker挂载到容器,但卷不属于联合文件系统(Union FileSystem),因此能够绕过联合文件系统提供一些用于持续存储或共享数据的特性: 卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷。 数据卷的特点: 数据卷可在容器之间共享或重用数据 卷中的更改可以直接生效 数据卷中的更改不会包含在镜像的更新中 数据卷的生命周期一直持续到没有容器使用它为止 ### 2、简单使用 运行容器,指定挂在数据卷命令: ``` docker run -it -v 主机目录:容器目录 如果没有目录,会自动创建 ``` 宿主机目录/tmp/test与容器/home建立数据卷,首先在容器目录下创建test.sh文件,再去宿主机目录下查看是否有该文件。 <img src="https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220326201747592.png" alt="image-20220326201747592" /> ![](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220326201825328.png) 查看容器对应元数据`docker inspect 容器id`,可以在Mounts节点查看建立的数据卷信息。 ![image-20220326202124402](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220326202124402.png) 即使容器停止运行或者容器删除,仍然可以实现数据同步,本地的数据卷不会丢失。 ### 3. MySQL容器建立数据卷同步数据 在Linux下的MySQL默认的数据文档存储目录为/var/lib/mysql,默认的配置文件位置/etc/mysql/conf.d,为了确保MySQL镜像或容器删除后,造成的数据丢失,下面建立数据卷保存MySQL的数据和文件。 ```java docker run -d -p 6603:3306 -v /data/mysql/conf:/etc/mysql/conf.d -v /data/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7.37 ``` 如果使用配置的密码连接mysql服务失败,原因很大可能是本机挂载的配置文件中已有文件,将容器中的配置给覆盖了,我们将相应的本机文件中的文件配置删除即可. ### 4. 常用命令 (1)创建数据卷 ```java docker volume create my-vol ``` (2)查看所有的数据卷 ```java docker volume ls local my-vol ``` (3)查看指定数据卷的信息 ```java docker volume inspect my-vol [ { "CreatedAt": "2022-03-26T20:33:27+08:00", "Driver": "local", "Labels": {}, "Mountpoint": "/var/lib/docker/volumes/my-vol/_data", "Name": "my-vol", "Options": {}, "Scope": "local" } ] ``` (4)删除数据卷 `docker volume rm ...` ```java docker volume rm my-vol ``` (5)删除容器之时删除相关的卷 ```java docker rm -f -v 容器id ``` **创建容器时指定宿主机映射目录的数据卷不会被删除,而没有指定的数据卷会被删除** 无主的数据卷可能会占据很多空间,要清理请使用以下命令 ```java docker volume prune ``` ### 5、volume与mount的总结 用户能够经过`docker run`的`--volume/-v`或`--mount`选项来建立带有数据卷的容器 命令用法 `--volume(-v)` 参数`--volume`(或简写为`-v`)只能建立bind mount。示例: ```java docker run --name $CONTAINER_NAME -it \ -v $PWD/$CONTAINER_NAME/app:/app:rw \ -v $PWD/$CONTAINER_NAME/data:/data:ro \ avocado-cloud:latest /bin/bash ``` 注释: - 命令格式:`[[HOST-DIR:]CONTAINER-DIR[:OPTIONS]]]` - 若是指定`HOST-DIR`则必须是绝对路径,若是路径不存在则会自动建立 - 实例中的`rw`为读写,`ro`为只读 `--mount` 参数`--mount`默认状况下用来挂载volume,但也能够用来建立bind mount和tmpfs。若是不指定`type`选项,则默认为挂载volume,volume是一种更为灵活的数据管理方式,volume能够经过`docker volume`命令集被管理。示例: ```java docker run --name $CONTAINER_NAME -it \ --mount type=bind,source=$PWD/$CONTAINER_NAME/app,destination=/app \ --mount source=${CONTAINER_NAME}-data,destination=/data,readonly \ avocado-cloud:latest /bin/bash ``` 注释: - 挂载volume命令格式:`[type=volume,]source=my-volume,destination=/path/in/container[,...]` - 建立bind mount命令格式:`type=bind,source=/path/on/host,destination=/path/in/container[,...]` - 若是建立bind mount并指定`source`则必须是绝对路径,**且路径必须已经存在** - 示例中`readonly`表示只读 差别总结 建立bind mount和挂载volume的比较 | 对比项 | bind mount | volume | | :----------- | :---------------- | :----------------------- | | Source位置 | 用户指定 | /var/lib/docker/volumes/ | | Source为空 | 覆盖dest为空 | 保留dest内容 | | Source非空 | 覆盖dest内容 | 覆盖dest内容 | | Source种类 | 文件或目录 | 只能是目录 | | 可移植性 | 通常(自行维护) | 强(docker托管) | | 宿主直接访问 | 容易(仅需chown) | 受限(需登录root用户)* | *注释:Docker没法简单地经过`sudo chown someuser: -R /var/lib/docker/volumes/somevolume`来将volume的内容开放给主机上的普通用户访问,若是开放更多权限则有安全风险。而这点上Podman的设计就要理想得多,volume存放在`$HOME/.local/share/containers/storage/volumes/`路径下,即提供了便捷性,又保障了安全性。无需root权限便可运行容器,这正是Podman的优点之一,实际使用过程当中的确受益良多。i 建立bind mount时使用`--volume`和`--mount`的比较 | 对比项 | `--volume` 或 `-v` | `--mount type=bind` | | :----------------: | :----------------- | :------------------ | | 若是主机路径不存在 | 自动建立 | 命令报错 | ### 6、具名挂载和匿名挂载 #### 1、匿名挂载 匿名挂载就是在指定数据卷的时候,不指定容器路径对应的主机路径,这样对应映射的主机路径就是默认的路径/var/lib/docker/volumes/中自动生成一个随机命名的文件夹。 ```java root@mycentos7 /$ docker run -d -P --name nginx01 -v /etc/nginx nginx Unable to find image 'nginx:latest' locally latest: Pulling from library/nginx a2abf6c4d29d: Pull complete a9edb18cadd1: Pull complete 589b7251471a: Pull complete 186b1aaa4aa6: Pull complete b4df32aa5a72: Pull complete a0bcbecc962e: Pull complete Digest: sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31 Status: Downloaded newer image for nginx:latest ad1fc76b3fb2407ffd35649d38354346bb2c122a1fce32ef46982ed2158eb9da root@mycentos7 /$ docker volume ls DRIVER VOLUME NAME local 2b83245656b2acbacd9085e22c2d3dff1edd7b65bb087f9fc586d44f337b2f5d root@mycentos7 /$ docker inspect nginx01 | grep 2b83245656b2acbacd9085e22c2d3dff1edd7b65bb087f9fc586d44f337b2f5d "Name": "2b83245656b2acbacd9085e22c2d3dff1edd7b65bb087f9fc586d44f337b2f5d", "Source": "/var/lib/docker/volumes/2b83245656b2acbacd9085e22c2d3dff1edd7b65bb087f9fc586d44f337b2f5d/_data", ``` #### 2、具名挂载 具名挂载,就是指定文件夹名称,区别于指定路径挂载,这里的指定文件夹名称是在Docker指定的默认数据卷路径下的。通过`docker volume ls`命令可以查看当前数据卷的目录情况。 ```java root@mycentos7 /$ docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx 2d1e751e538a8b2e4ab3348bff248227cc046438624a7033ee37f090ece83a0c root@mycentos7 /$ docker volume ls DRIVER VOLUME NAME local 2b83245656b2acbacd9085e22c2d3dff1edd7b65bb087f9fc586d44f337b2f5d local juming-nginx root@mycentos7 /$ docker inspect nginx02 | grep juming-nginx "juming-nginx:/etc/nginx" "Name": "juming-nginx", "Source": "/var/lib/docker/volumes/juming-nginx/_data", ``` 查看指定的数据卷信息的命令:`docker volume inspect数据卷名称` ```java root@mycentos7 /$ docker volume inspect juming-nginx [ { "CreatedAt": "2022-03-26T22:06:15+08:00", "Driver": "local", "Labels": null, "Mountpoint": "/var/lib/docker/volumes/juming-nginx/_data", "Name": "juming-nginx", "Options": null, "Scope": "local" } ] ``` 可以看到主机数据卷挂载在/var/lib/docker/volumes/juming-nginx/_data上 Docker所有的数据卷默认在/var/lib/docker/volumes/ 目录下 匿名挂载,具名挂载,指定路径挂载的命令区别如下: -v 容器内路径 #匿名挂载 -v 卷名:容器内路径 #具名挂载 -v /宿主机路径:容器内路径 #指定路径挂载 指定数据卷映射的相关参数: ro —— readonly 只读。设置了只读则只能操作宿主机的路径,不能操作容器中的对应路径。 rw —— readwrite 可读可写 ```java # docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:ro nginx # docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:rw nginx ``` ### 7、Dockerfile中设置数据卷 我们可以在Dockerfile中使用VOLUME指令来给镜像添加一个或多个数据卷。 下面使用Dockerfile构建一个新的镜像,dockerfile01文件的内容,匿名挂载了volume01和volume02两个目录: vim /data/dockerfile01 ```java FROM centos VOLUME ["volume01","volume02"] CMD echo "----end----" CMD /bin/bash ``` 执行构建镜像 ```java root@mycentos7 /data$ docker build -f /data/dockerfile01 -t grrrrlj/centos:1.0 . Sending build context to Docker daemon 219.9MB Step 1/4 : FROM centos ---> 5d0da3dc9764 Step 2/4 : VOLUME ["volume01","volume02"] ---> Using cache ---> 3939acadface Step 3/4 : CMD echo "----end----" ---> Using cache ---> 8d4bb48829c8 Step 4/4 : CMD /bin/bash ---> Using cache ---> a0a920b6fb02 Successfully built a0a920b6fb02 Successfully tagged grrrrlj/centos:1.0 root@mycentos7 /data$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE ethan/centos 1.0 a0a920b6fb02 55 seconds ago 231MB grrrrlj/centos 1.0 a0a920b6fb02 55 seconds ago 231MB mysql 5.7.37 05311a87aeb4 8 days ago 450MB hwdsl2/ipsec-vpn-server latest f19f5bbe6abd 2 months ago 44MB nginx latest 605c77e624dd 2 months ago 141MB hello-world latest feb5d9fea6a5 6 months ago 13.3kB centos latest 5d0da3dc9764 6 months ago 231MB ``` 完成镜像的生成后,启动自己生成的容器 ```java root@mycentos7 /data$ docker run -it a0a920b6fb02 /bin/bash [root@cbf58bdb178c /]# ls -l total 4 lrwxrwxrwx 1 root root 7 Nov 3 2020 bin -> usr/bin drwxr-xr-x 5 root root 360 Mar 26 14:20 dev drwxr-xr-x 1 root root 66 Mar 26 14:20 etc drwxr-xr-x 2 root root 6 Nov 3 2020 home lrwxrwxrwx 1 root root 7 Nov 3 2020 lib -> usr/lib lrwxrwxrwx 1 root root 9 Nov 3 2020 lib64 -> usr/lib64 drwx------ 2 root root 6 Sep 15 2021 lost+found drwxr-xr-x 2 root root 6 Nov 3 2020 media drwxr-xr-x 2 root root 6 Nov 3 2020 mnt drwxr-xr-x 2 root root 6 Nov 3 2020 opt dr-xr-xr-x 172 root root 0 Mar 26 14:20 proc dr-xr-x--- 2 root root 162 Sep 15 2021 root drwxr-xr-x 11 root root 163 Sep 15 2021 run lrwxrwxrwx 1 root root 8 Nov 3 2020 sbin -> usr/sbin drwxr-xr-x 2 root root 6 Nov 3 2020 srv dr-xr-xr-x 13 root root 0 Mar 26 14:20 sys drwxrwxrwt 7 root root 171 Sep 15 2021 tmp drwxr-xr-x 12 root root 144 Sep 15 2021 usr drwxr-xr-x 20 root root 4096 Sep 15 2021 var drwxr-xr-x 2 root root 6 Mar 26 14:20 volume01 drwxr-xr-x 2 root root 6 Mar 26 14:20 volume02 ``` 可以看到自动挂载的数据卷目录。下面查看对应宿主机的数据卷目录 ```java root@mycentos7 ~$ docker ps 127 ↵ CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 40549a6e8b0f a0a920b6fb02 "/bin/bash" 38 seconds ago Up 37 seconds vibrant_proskuriakova 2d1e751e538a nginx "/docker-entrypoint.…" 16 minutes ago Up 16 minutes 0.0.0.0:49154->80/tcp, :::49154->80/tcp nginx02 ad1fc76b3fb2 nginx "/docker-entrypoint.…" 20 minutes ago Up 20 minutes 0.0.0.0:49153->80/tcp, :::49153->80/tcp nginx01 root@mycentos7 ~$ docker inspect 40549a6e8b0f "Mounts": [ { "Type": "volume", "Name": "432081a42ca914349960a69467147a8063c52e82af6ce6803efa138397e9d87d", "Source": "/var/lib/docker/volumes/432081a42ca914349960a69467147a8063c52e82af6ce6803efa138397e9d87d/_data", "Destination": "volume02", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" }, { "Type": "volume", "Name": "ebad18b53c195a0dfdcacf900ddc37fd0094a681201135a09b1f96868d96da2c", "Source": "/var/lib/docker/volumes/ebad18b53c195a0dfdcacf900ddc37fd0094a681201135a09b1f96868d96da2c/_data", "Destination": "volume01", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" } ], ``` 可以看到Mounts下有宿主机的挂载目录。因为dockerfile中没有指定宿主机目录,所以属于匿名挂载,在/var/lib/docker/volumes/目录下生成了随机命名的路径。 ### 8、 容器数据卷 容器数据卷是指建立数据卷,来同步多个容器间的数据,实现容器间的数据同步。 父子容器数据卷的内容实时同步。 首先启动容器1,volume01、volume02为挂载目录。 ```java [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker run -it --name centos01 grrrrlj/centos:1.0 [root@731d53b8c3d5 /]# ls bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var volume01 volume02 ``` 然后启动容器2,通过参数`--volumes-from`,设置容器2和容器1建立数据卷挂载关系。 ```java [root@iZwz99sm8v95sckz8bd2c4Z /]# docker run -it --name centos02 --volumes-from centos01 grrrrlj/centos:1.0 [root@7f90d4147511 /]# ls -l total 56 lrwxrwxrwx 1 root root 7 Nov 3 15:22 bin -> usr/bin drwxr-xr-x 5 root root 360 Dec 30 14:58 dev drwxr-xr-x 1 root root 4096 Dec 30 14:58 etc drwxr-xr-x 2 root root 4096 Nov 3 15:22 home lrwxrwxrwx 1 root root 7 Nov 3 15:22 lib -> usr/lib lrwxrwxrwx 1 root root 9 Nov 3 15:22 lib64 -> usr/lib64 drwx------ 2 root root 4096 Dec 4 17:37 lost+found drwxr-xr-x 2 root root 4096 Nov 3 15:22 media drwxr-xr-x 2 root root 4096 Nov 3 15:22 mnt drwxr-xr-x 2 root root 4096 Nov 3 15:22 opt dr-xr-xr-x 108 root root 0 Dec 30 14:58 proc dr-xr-x--- 2 root root 4096 Dec 4 17:37 root drwxr-xr-x 11 root root 4096 Dec 4 17:37 run lrwxrwxrwx 1 root root 8 Nov 3 15:22 sbin -> usr/sbin drwxr-xr-x 2 root root 4096 Nov 3 15:22 srv dr-xr-xr-x 13 root root 0 Dec 29 15:41 sys drwxrwxrwt 7 root root 4096 Dec 4 17:37 tmp drwxr-xr-x 12 root root 4096 Dec 4 17:37 usr drwxr-xr-x 20 root root 4096 Dec 4 17:37 var drwxr-xr-x 2 root root 4096 Dec 30 14:54 volume01 drwxr-xr-x 2 root root 4096 Dec 30 14:54 volume02 ``` 首先在容器2中的volume01中添加文件,然后就可以看到容器1的文件也会添加上了 下面同步两个MySQL的数据库和配置文件,与上面的操作相同,首先建立数据卷,然后给另一个MySQL容器建立容器数据卷挂载,示例: ```java [root@iZwz99sm8v95sckz8bd2c4Z home]# docker run -d -p 6603:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7 [root@iZwz99sm8v95sckz8bd2c4Z home]# docker run -d -p 6604:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volumes-from mysql01 mysql:5.7 ``` ## Dockerfile ### 1、Dockerfile介绍 Dockerfile是用来构建Docker镜像的文本文件,也可以说是命令参数脚本。docker build命令用于从Dockerfile构建镜像。可以在docker build命令中使用-f标志指向文件系统中任何位置的Dockerfile。 Docker镜像发布的步骤: 1、编写一个dockerfile文件 2、docker build 构建成为一个镜像 3、docker run 镜像 4、docker push 镜像(发布镜像到DockerHub、阿里云镜像仓库) 示例一个镜像的结构图: ![image-20220326232012442](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220326232012442.png) ### 2、Dockerfile指令说明 | 指令 | 说明 | | ---------- | ------------------------------------------------------------ | | FROM | 指定基础镜像 | | MAINTAINER | 镜像是谁写的,姓名+邮箱 | | RUN | 镜像构建的时候需要运行的命令 | | ADD | 将本地文件添加到容器中,tar类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似wget | | WORKDIR | 镜像的工作目录 | | VOLUME | 挂载的目录 | | EXPOSE | 保留端口配置 | | CMD | 指定这个容器启动的时候要运行的命令(只有最后一个会生效) | | EMTRYPOINT | 指定这个容器启动的时候要运行的命令,可以追加命令 | | ONBUILD | 当构建一个被继承DockerFile,这个时候就会运行ONBUILD的指令,触发指令 | | COPY | 功能类似ADD,但是是不会自动解压文件,也不能访问网络资源 | | ENV | 构建的时候设置环境变量 | 一个形象的解释各个指令作用的图: ![image-20220326232257951](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220326232257951.png) Dockerfile 一般分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令,’#’ 为 Dockerfile 中的注释。 关于DockerFile文件的脚本注意点有: 1、每个保留关键字(指令)都必须是大写字母 2、文件中的指令从上到下顺序执行,第一个指令必须是FROM 3、# 号表示注释 4、每一个指令都会创建提交一个新的镜像层,并提交! 关于Dockerfile指令的详细语法解释:Dockerfile文件详解 Dockerfile指令介绍的官方文档:https://docs.docker.com/engine/reference/builder/ ### 3. 制作Centos镜像 下面通过编写Dockerfile文件来制作Centos镜像,并在官方镜像的基础上添加vim和net-tools工具。首先在/home/dockfile 目录下新建文件mydockerfile-centos。然后使用上述指令编写该文件。 ```java [root@iZwz99sm8v95sckz8bd2c4Z dockerfile]# cat mydockerfile-centos FROM centos MAINTAINER ethan<1258398543@qq.com> ENV MYPATH /usr/local WORKDIR $MYPATH RUN yum -y install vim RUN yum -y install net-tools EXPOSE 80 CMD echo $MYPATH CMD echo "---end---" CMD /bin/bash ``` 逐行解释该Dockerfile文件的指令: FROM centos:该image文件继承官方的centos,后面加冒号如centos:7,用于指定镜像的版本 ENV MYPATH /usr/local:设置环境变量MYPATH ,后面有用到 WORKDIR $MYPATH:直接使用上面设置的环境变量,指定/usr/local为工作目录 RUN yum -y install vim和RUN yum -y install net-tools:在/usr/local目录下,运行yum -y install vim和yum -y install net-tools命令安装工具,注意安装后的所有依赖和工具都会打包到image文件中 EXPOSE 80:将容器80端口暴露出来,允许外部连接这个端口 CMD:指定容器启动的时候运行命令 通过这个dockerfile构建镜像,构建镜像命令:docker build -f dockerfile文件路径 -t 镜像名[:版本号] .(这里有个小点.) 上面命令中,-t参数用来指定 image 文件的名字,后面还可以用冒号指定标签。如果不指定,默认的标签就是latest。最后的那个点表示 Dockerfile 文件所在的路径,上例是当前路径,所以是一个点。 下面执行build命令生成image文件,如果执行成功,可以通过docker images来查看新生成的镜像文件。 ```java [root@iZwz99sm8v95sckz8bd2c4Z dockerfile]# docker build -f mydockerfile-centos -t mycentos:1.0 . Sending build context to Docker daemon 2.048kB Step 1/10 : FROM centos ---> 300e315adb2f Step 2/10 : MAINTAINER ethan<1258398543@qq.com> ---> Running in bbfd1a4949e1 Removing intermediate container bbfd1a4949e1 ---> b2b6851e55fa Step 3/10 : ENV MYPATH /usr/local ---> Running in 7cd8c84a5b70 Removing intermediate container 7cd8c84a5b70 ---> e27a56b5247b Step 4/10 : WORKDIR ${MYPATH} ---> Running in 770e175260c4 Removing intermediate container 770e175260c4 ---> 7d46880ef0fd Step 5/10 : RUN yum -y install vim Step 7/10 : EXPOSE 80 ---> Running in 24cfcfa56460 Removing intermediate container 24cfcfa56460 ---> 2064c1540e8e Step 8/10 : CMD echo ${MYPATH} ---> Running in 83dd9766da3c Removing intermediate container 83dd9766da3c ---> 9b8661c812c4 Step 9/10 : CMD echo "---end---" ---> Running in 974afa805b27 Removing intermediate container 974afa805b27 ---> 83459c404586 Step 10/10 : CMD /bin/bash ---> Running in 4fba3174f9d8 Removing intermediate container 4fba3174f9d8 ---> 1185a46e3a12 Successfully built 1185a46e3a12 Successfully tagged mycentos:1.0 [root@iZwz99sm8v95sckz8bd2c4Z dockerfile]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE mycentos 1.0 1185a46e3a12 4 minutes ago 291MB ``` 下面生成容器,测试相关命令,查看默认工作目录是否设置成功,vim和net-tools工具是否下载成功。 ```java [root@iZwz99sm8v95sckz8bd2c4Z dockerfile]# docker run -it mycentos:1.0 [root@e548efe82b30 local]# pwd /usr/local #net-tools工具提供ifconfig命令 [root@e548efe82b30 local]# ifconfig eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.17.0.3 netmask 255.255.0.0 broadcast 172.17.255.255 ether 02:42:ac:11:00:03 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 lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 loop txqueuelen 1000 (Local Loopback) 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 [root@e548efe82b30 local]# vi test.txt [root@e548efe82b30 local]# cat test.txt Hello world! ``` 另外,我们通过`docker history 容器id`命令来查看镜像的构建步骤 ```java [root@iZwz99sm8v95sckz8bd2c4Z dockerfile]# docker history 1185a46e3a12 IMAGE CREATED CREATED BY SIZE COMMENT 1185a46e3a12 13 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "/bin… 0B 83459c404586 13 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "echo… 0B 9b8661c812c4 13 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "echo… 0B 2064c1540e8e 13 minutes ago /bin/sh -c #(nop) EXPOSE 80 0B 1b15d4a1fd5e 13 minutes ago /bin/sh -c yum -y install net-tools 23.3MB 9336c20f0b6d 13 minutes ago /bin/sh -c yum -y install vim 58MB 7d46880ef0fd 14 minutes ago /bin/sh -c #(nop) WORKDIR /usr/local 0B e27a56b5247b 14 minutes ago /bin/sh -c #(nop) ENV MYPATH=/usr/local 0B b2b6851e55fa 14 minutes ago /bin/sh -c #(nop) MAINTAINER ethan<12583985… 0B 300e315adb2f 3 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B <missing> 3 weeks ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B <missing> 3 weeks ago /bin/sh -c #(nop) ADD file:bd7a2aed6ede423b7… 209MB ``` ### 4、RUN,CMD和ENTRYPOINT的区别 简单说,RUN命令在 image 文件的构建阶段执行,执行结果都会打包进入 image 文件;CMD命令则是在容器启动后执行。另外,一个 Dockerfile 可以包含多个RUN命令,但是只能有一个CMD命令。 注意,指定了CMD命令以后,docker container run命令就不能附加命令了(比如前面的/bin/bash),否则它会覆盖CMD命令。 CMD和ENTRYPOINT的区别在哪里? CMD :指定容器启动的时候要运行的命令,只有最后一个会生效 ENTRYPOINT :指定容器启动的时候要运行的命令,命令可以追加 首先是使用CMD指令 ```java [root@iZwz99sm8v95sckz8bd2c4Z dockerfile]# cat dockerfile-cmd-test FROM centos CMD ["ls","-a"] [root@iZwz99sm8v95sckz8bd2c4Z dockerfile]# docker build -f dockerfile-cmd-test -t cmdtest:1.0 . Sending build context to Docker daemon 3.072kB Step 1/2 : FROM centos ---> 300e315adb2f Step 2/2 : CMD ["ls","-a"] ---> Running in 6d4d0112322f Removing intermediate container 6d4d0112322f ---> b6ec5224d2ac Successfully built b6ec5224d2ac Successfully tagged cmdtest:1.0 [root@iZwz99sm8v95sckz8bd2c4Z dockerfile]# docker run cmdtest:1.0 . .. .dockerenv bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var #由于使用的是 CMD指令,命令无追加,-l取代了原本的ls -a,而-l命令不存在所以报错。 [root@iZwz99sm8v95sckz8bd2c4Z dockerfile]# docker run cmdtest:1.0 -l docker: Error response from daemon: OCI runtime create failed: container_linux.go:370: starting container process caused: exec: "-l": executable file not found in $PATH: unknown. ``` 可以看到追加命令-l 出现了报错。 下面使用ENTRYPOINT来构建一个镜像 ```java #1.修改dockerfile文件 [root@iZwz99sm8v95sckz8bd2c4Z dockerfile]# cat dockerfile-cmd-test FROM centos ENTRYPOINT ["ls","-a"] #2.构建镜像 [root@iZwz99sm8v95sckz8bd2c4Z dockerfile]# docker build -f dockerfile-cmd-test -t cmdtest:2.0 . Sending build context to Docker daemon 3.072kB Step 1/2 : FROM centos ---> 300e315adb2f Step 2/2 : ENTRYPOINT ["ls","-a"] ---> Running in 61389c0c1967 Removing intermediate container 61389c0c1967 ---> ac7b7e83ff88 Successfully built ac7b7e83ff88 Successfully tagged cmdtest:2.0 #3.运行镜像 [root@iZwz99sm8v95sckz8bd2c4Z dockerfile]# docker run cmdtest:2.0 . .. .dockerenv bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var #4.追加镜像再次运行 [root@iZwz99sm8v95sckz8bd2c4Z dockerfile]# docker run cmdtest:2.0 -l total 56 drwxr-xr-x 1 root root 4096 Jan 1 03:55 . drwxr-xr-x 1 root root 4096 Jan 1 03:55 .. -rwxr-xr-x 1 root root 0 Jan 1 03:55 .dockerenv lrwxrwxrwx 1 root root 7 Nov 3 15:22 bin -> usr/bin drwxr-xr-x 5 root root 340 Jan 1 03:55 dev drwxr-xr-x 1 root root 4096 Jan 1 03:55 etc drwxr-xr-x 2 root root 4096 Nov 3 15:22 home lrwxrwxrwx 1 root root 7 Nov 3 15:22 lib -> usr/lib lrwxrwxrwx 1 root root 9 Nov 3 15:22 lib64 -> usr/lib64 drwx------ 2 root root 4096 Dec 4 17:37 lost+found drwxr-xr-x 2 root root 4096 Nov 3 15:22 media drwxr-xr-x 2 root root 4096 Nov 3 15:22 mnt drwxr-xr-x 2 root root 4096 Nov 3 15:22 opt dr-xr-xr-x 106 root root 0 Jan 1 03:55 proc dr-xr-x--- 2 root root 4096 Dec 4 17:37 root drwxr-xr-x 11 root root 4096 Dec 4 17:37 run lrwxrwxrwx 1 root root 8 Nov 3 15:22 sbin -> usr/sbin drwxr-xr-x 2 root root 4096 Nov 3 15:22 srv dr-xr-xr-x 13 root root 0 Dec 29 15:41 sys drwxrwxrwt 7 root root 4096 Dec 4 17:37 tmp drwxr-xr-x 12 root root 4096 Dec 4 17:37 usr drwxr-xr-x 20 root root 4096 Dec 4 17:37 var ``` ### 5. 制作Tomcat镜像并发布镜像 #### 1、制作Tomcat镜像 1.准备镜像文件tomcat、jdk压缩包 ```java [root@iZwz99sm8v95sckz8bd2c4Z tomcat]# vi readme.txt [root@iZwz99sm8v95sckz8bd2c4Z tomcat]# ll total 200700 -rw-r--r-- 1 root root 10371538 Jan 1 16:11 apache-tomcat-8.5.55.tar.gz -rw-r--r-- 1 root root 195132576 Jan 1 16:13 jdk-8u251-linux-x64.tar.gz -rw-r--r-- 1 root root 20 Jan 1 16:14 readme.txt ``` 2.编写dockerfile文件,文件名使用官方命名:Dockerfile ,build的时候会默认寻找当前目录下的文件,不需要使用-f参数指定 ```java [root@iZwz99sm8v95sckz8bd2c4Z tomcat]# vim Dockerfile [root@iZwz99sm8v95sckz8bd2c4Z tomcat]# cat Dockerfile FROM centos MAINTAINER ethan<1258398543@qq.com> COPY readme.txt /usr/local/readme.txt ADD jdk-8u251-linux-x64.tar.gz /usr/local/ ADD apache-tomcat-8.5.55.tar.gz /usr/local/ RUN yum -y install vim ENV MYPATH /usr/local WORKDIR $MYPATH ENV JAVA_HOME /usr/local/jdk1.8.0_251 ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar ENV CATALINA_HOME /usr/local/apache-tomcat-8.5.55 ENV CATALINA_BASH /usr/local/apache-tomcat-8.5.55 ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin EXPOSE 8080 CMD /usr/local/apache-tomcat-8.5.55/bin/startup.sh && tail -F /usr/local/apache-tomcat-8.5.55/bin/logs/catalina.out ``` 3.使用该Dockerfile构建镜像 ```java [root@iZwz99sm8v95sckz8bd2c4Z tomcat]# docker build -t diytomcat:1.0 . ``` 4.启动生成的镜像,构建Tomcat容器. 这里设置了数据卷,宿主机的/home/dockerfile/tomcat/test对应该容器的/usr/local/apache-tomcat-8.5.55/webapps/test。这样关于test项目的修复只需要在宿主机上修改就可以了,不需要进入到容器中修改。 ```java [root@iZwz99sm8v95sckz8bd2c4Z tomcat]# docker run -d -p 8088:8080 --name diytomcat -v /home/dockerfile/tomcat/test:/usr/local/apache-tomcat-8.5.55/webapps/test diytomcat:1.0 ``` 5.在/home/dockerfile/tomcat/test的目录下,新建index.html 测试Tomcat是否能正常使用。 因为设置了卷挂载所以可以直接在宿主机中进行操作。 ```html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <title>这是个标题</title> </head> <body> <h1>这是一个一个简单的HTML</h1> <p>Hello World!</p> </body> </html> ``` 6.访问测试,浏览器访问查看是否能正常访问 ![image-20220326234026827](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220326234026827.png) 如果页面显示乱码,就需要修改tomcat的server.xml文件 ```java <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8" /> ``` 这里是添加了一个属性:URIEncoding,将该属性值设置为UTF-8,即可让Tomcat(默认ISO-8859-1编码)以UTF-8的编码处理get请求。 #### 2、发布镜像到DockerHub 1.登录https://hub.docker.com/ DockerHub官网进行注册 2.进行登录,docker login -u 用户名 登录命令 ```java [root@iZwz99sm8v95sckz8bd2c4Z test]# docker login --help Usage: docker login [OPTIONS] [SERVER] Log in to a Docker registry. If no server is specified, the default is defined by the daemon. Options: -p, --password string Password --password-stdin Take the password from stdin -u, --username string Usernames ``` 3.使用`docker push`命令推送镜像到DockerHub上的仓库 ```java [root@iZwz99sm8v95sckz8bd2c4Z test]# docker tag 0975df661526 ethanhuang824/diytomcat:1.0 [root@iZwz99sm8v95sckz8bd2c4Z test]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE diytomcat 1.0 0975df661526 About an hour ago 688MB ethanhuang824/diytomcat 1.0 0975df661526 About an hour ago 688MB cmdtest 2.0 ac7b7e83ff88 6 hours ago 209MB cmdtest 1.0 b6ec5224d2ac 6 hours ago 209MB ethan/centos 1.0 1df90e6fd790 2 days ago 209MB mytomcat 1.0 f189aac861de 3 days ago 653MB mysql 5.7 f07dfa83b528 10 days ago 448MB tomcat latest feba8d001e3f 2 weeks ago 649MB nginx latest ae2feff98a0c 2 weeks ago 133MB centos latest 300e315adb2f 3 weeks ago 209MB elasticsearch 7.6.2 f29a1ee41030 9 months ago 791MB [root@iZwz99sm8v95sckz8bd2c4Z test]# docker push ethanhuang824/diytomcat:1.0 ``` 因为push的时候,镜像名前面需要加上用户名(ethanhuang824是我的用户名。如果用户名不是当前登录用户则会拒绝push请求),所以需要使用命令docker tag 镜像名 新的镜像名复制出一份镜像重新打个Tag。 ### 6、发布镜像到阿里云容器服务 1.登录阿里云,找到容器镜像服务 2.创建命名空间 3.上传镜像 ## Docker网络 清空镜像及容器 ### 1、Docker默认的网络模式 使用以下命令查看所有的Docker网络: ```java docker network ls ``` Docker默认提供了四个网络模式,说明: bridge:容器默认的网络是桥接模式(自己搭建的网络默认也是使用桥接模式,启动容器默认也是使用桥接模式)。此模式会为每一个容器分配、设置IP等,并将容器连接到一个docker0虚拟网桥,通过docker0网桥以及Iptables nat表配置与宿主机通信。 none:不配置网络,容器有独立的Network namespace,但并没有对其进行任何网络设置,如分配veth pair 和网桥连接,配置IP等。 host:容器和宿主机共享Network namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。 container:创建的容器不会创建自己的网卡,配置自己的IP容器网络连通。容器和另外一个容器共享Network namespace(共享IP、端口范围)。 容器默认使用bridge网络模式,我们使用该docker run --network=选项指定容器使用的网络: host模式:使用 --net=host 指定。 none模式:使用 --net=none 指定。 bridge模式:使用 --net=bridge 指定,默认设置。 container模式:使用 --net=container:NAME_or_ID 指定。 1.1 host模式 Namespace的简要说明: Docker使用了Linux的Namespaces技术来进行资源隔离,如PID Namespace隔离进程,Mount Namespace隔离文件系统,Network Namespace隔离网络等。 一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的NetworkNamespace隔离。一个Docker容器一般会分配一个独立的Network Namespace。 如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。 使用host模式的容器可以直接使用宿主机的IP地址与外界通信,容器内部的服务端口也可以使用宿主机的端口,不需要进行NAT,host最大的优势就是网络性能比较好,但是docker host上已经使用的端口就不能再用了,网络的隔离性不好。Host模式的模型图,如下图所示: ![image-20220326235809689](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220326235809689.png) 备注:eth0为宿主机的10.126.130.4为宿主机的内网地址。 1.2 container模式 这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。Container模式模型示意图如下: ![image-20220327001400976](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327001400976.png) 1.3 none模式 使用none模式,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。 这种网络模式下容器只有lo回环网络,没有其他网卡。none模式可以在容器创建时通过–network=none来指定。这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性。 None模式示意图: ![image-20220327001449790](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327001449790.png) 1.4 bridge模式 当Docker进程启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。 从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。在主机上创建一对虚拟网卡veth pair设备,Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0(容器的网卡),另一端放在主机中,以vethxxx这样类似的名字命名,并将这个网络设备加入到docker0网桥中。可以通过brctl show命令查看。 bridge模式是docker的默认网络模式,不写–net参数,就是bridge模式。使用docker run -p时,docker实际是在iptables做了DNAT规则,实现端口转发功能。可以使用iptables -t nat -vnL查看。bridge模式如下图所示: ![image-20220327001526911](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327001526911.png) 当Docker server启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。Docker0使用到的技术是evth-pair技术。在默认bridge网络模式下,我们每启动一个Docker容器,Docker就会给Docker容器配置一个ip。 Docker容器完成bridge网络配置的过程如下: 在主机上创建一对虚拟网卡veth pair设备。veth设备总是成对出现的,它们组成了一个数据的通道,数据从一个设备进入,就会从另一个设备出来。因此,veth设备常用来连接两个网络设备。 Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0。另一端放在主机中,以veth65f9这样类似的名字命名,并将这个网络设备加入到docker0网桥中。 从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。 执行命令 ip addr,可以看到安装Docker后,默认宿主机有三个地址。 ![image-20220327001556085](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327001556085.png) Docker是如何处理容器网络访问的呢?启动一个容器,进入容器使用`ip addr`查看网络设置 ```java docker run -d -P --name tomcat01 tomcat:8.0 ``` ![image-20220327001715895](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327001715895.png) 可以看到容器启动后Docker分配给容器一个格式如eth0@if262 的ip地址。宿主机运行`ip addr`命令查看变化: ![image-20220327001753100](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327001753100.png) 可以看到容器内部和Linux主机都会创建一个新的网卡,而这两个网卡都是成对的。使用的技术就是evth-pair。evth-pair 就是一对的虚拟设备接口,他们是成对出现的,一段连着协议,一段彼此相连。evth-pair充当一个桥梁,连接各种虚拟网络设备。 ==Linux主机可以直接网络连接Docker容器。Docker容器和容器之间同样可以直接网络连接的。==下面具体实验验证一下。 ```java docker exec -it tomcat01 ip addr ``` ![image-20220327001844055](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327001844055.png) 下面再启动一个Tomcat容器,尝试容器之间的网络连接是否能够成功。 ![image-20220327001908746](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327001908746.png) ![image-20220327001924145](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327001924145.png) 尝试在tomcat01容器中ping tomcat02容器,可以看到两个容器是可以连接上的。两个Tomcat容器之间的网络交互模型图如下: ![image-20220327001945783](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327001945783.png) 说明: Tomcat01和Tomcat02都使用公用的路由器docker0。所有的容器不指定网络下,都是由docker0路由的,Docker会给我们容器默认分配一个随机的可用IP地址。 容器网络互联的通用模型,如下图所示: ![image-20220327002004973](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327002004973.png) Docker中的所有的网络接口都是虚拟的。只要容器删除,容器对应的网桥也会删除。 ```shell # 我们发现这个容器带来网卡,都是一对对的 # evth-pair 就是一对的虚拟设备接口,他们都是成对出现的,一段连着协议,一段彼此相连 # 正因为有这个特性,evth-pair充当一个桥梁,连接各种虚拟网络设备 # OpenStac,Docker容器之间的连接,OVS的连接,都是使用evth-pair技术 ``` ### 2、容器互联 在微服务部署的场景下,注册中心是使用服务名来唯一识别微服务的,而我们上线部署的时候微服务对应的IP地址可能会改动,所以我们需要使用容器名来配置容器间的网络连接。使用–link可以完成这个功能。 首先不设置连接的情况下,是无法通过容器名来进行连接的。 ![image-20220327002049758](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327002049758.png) 下面启动一个Tomcat容器Tomcat03使用–link 连接已经启动的Tomcat02容器。这样容器Tomcat03就可以通过容器名Tomcat02连接到容器Tomcat02。 ![image-20220327002206196](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327002206196.png) 但是反过来容器Tomcat02通过容器名Tomcat03直接ping容器Tomcat03是不行的。 ![image-20220327002241567](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327002241567.png) 这是因为`--link`的原理是在指定运行的容器上的/etc/hosts文件中添加容器名和ip地址的映射,如下: ![image-20220327002303418](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327002303418.png) 而tomcat02容器不能够通过容器名连接tomcat03是因为tomcat02容器中并没有添加容器名tomcat03和ip地址的映射. ![image-20220327002350811](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327002350811.png) 目前–link设置容器互连的方式已经不推荐使用。因为docker0不支持容器名访问,所以更多地选择自定义网络。 ### 3、自定义网络 因为docker0,默认情况下不能通过容器名进行访问。需要通过--link进行设置连接。这样的操作比较麻烦,更推荐的方式是自定义网络,容器都使用该自定义网络,就可以实现通过容器名来互相访问了。 下面查看network的相关命令 ```java docker network --help ``` ![image-20220327002613622](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327002613622.png) 查看默认的网络bridge的详细信息 ![image-20220327002640082](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327002640082.png) 查看 network create命令的相关参数 ![image-20220327002729446](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327002729446.png) 下面自定义一个网络: ```java docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet ``` ![image-20220327002805747](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327002805747.png) 参数说明: ```java --driver bridge #指定bridge驱动程序来管理网络 --subnet 192.168.0.0/16 #指定网段的CIDR格式的子网 --gateway 192.168.0.1 #指定主子网的IPv4或IPv6网关 ``` 网络mynet创建成功后,查看网络信息: ```java docker network inspect mynet ``` ![image-20220327002911235](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327002911235.png) 下面启动两个容器,指定使用该自定义网络mynet,测试处于自定义网络下的容器,是否可以直接通过容器名进行网络访问。 ```java docker run -d -P --name tomcat-net-01 --net mynet tomcat docker run -d -P --name tomcat-net-02 --net mynet tomcat docker network inspect mynet ``` ![image-20220327003019353](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327003019353.png) 下面通过容器名来测试容器tomcat-net-01 和容器tomcat-net-02之间是否能正常网络通信。 ![image-20220327003057995](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327003057995.png) 可以发现,在我们的自定义网络下,容器之间既可以通过容器名也可以通过ip地址进行网络通信。 我们自定义的网络默认已经帮我们维护了容器间的网络通信问题,这是实现网络互联的推荐方式。 ### 4、Docker网络之间的互联 没有设置的情况下,不同网络间的容器是无法进行网络连接的。如图,两个不同的网络docker0和自定义网络mynet的网络模型图: ![image-20220327003153990](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327003153990.png) 在默认网络bridge下启动容器tomcat-01,尝试连接mynet网络下的tomcat-net-01容器。 ![image-20220327003219996](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327003219996.png) 可以看到是无法网络连接的。不同Docker网络之间的容器需要连接的话需要把作为调用方的容器注册一个ip到被调用方所在的网络上。需要使用docker connect命令。 下面设置容器tomcat-01连接到mynet网络上。并查看mynet的网络详情,可以看到给容器tomcat-01分配了一个ip地址。 ```java docker network connect mynet tomcat-01 ``` ![image-20220327003307967](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327003307967.png) 设置完成后我们就可以实现不同网络之间的容器互联了。 ![image-20220327003331507](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327003331507.png) ### 5. Docker网络实战练习 #### Redis集群部署 下面部署如图所示的三主三从的Redis集群 ![image-20220327003352642](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327003352642.png) 首先停掉所有的容器 ```java docker rm -f $(docker ps -aq) ``` ![image-20220327003431014](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327003431014.png) 创建网络名为redis的自定义网络 ```bash docker network create redis --subnet 172.38.0.0/16 ``` ![image-20220327003449817](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327003449817.png) 通过以下脚本创建六个Redis 的配置信息: ```java for port in $(seq 1 6); \ do \ mkdir -p /mydata/redis/node-${port}/conf touch /mydata/redis/node-${port}/conf/redis.conf cat << EOF >/mydata/redis/node-${port}/conf/redis.conf port 6379 bind 0.0.0.0 cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 cluster-announce-ip 172.38.0.1${port} cluster-announce-port 6379 cluster-announce-bus-port 16379 appendonly yes EOF done ``` ![image-20220327003519180](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327003519180-20220327004505624.png) 下面启动6个Redis容器,设置对应的容器数据卷挂载, ```java #第1个Redis容器 docker run -p 6371:6379 -p 16371:16379 --name redis-1 \ -v /mydata/redis/node-1/data:/data \ -v /mydata/redis/node-1/conf/redis.conf:/etc/redis/redis.conf \ -d --net redis --ip 172.38.0.11 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf #第2个Redis容器 docker run -p 6372:6379 -p 16372:16379 --name redis-2 \ -v /mydata/redis/node-2/data:/data \ -v /mydata/redis/node-2/conf/redis.conf:/etc/redis/redis.conf \ -d --net redis --ip 172.38.0.12 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf #第3个Redis容器 docker run -p 6373:6379 -p 16373:16379 --name redis-3 \ -v /mydata/redis/node-3/data:/data \ -v /mydata/redis/node-3/conf/redis.conf:/etc/redis/redis.conf \ -d --net redis --ip 172.38.0.13 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf #第4个Redis容器 docker run -p 6374:6379 -p 16374:16379 --name redis-4 \ -v /mydata/redis/node-4/data:/data \ -v /mydata/redis/node-4/conf/redis.conf:/etc/redis/redis.conf \ -d --net redis --ip 172.38.0.14 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf #第5个Redis容器 docker run -p 6375:6379 -p 16375:16379 --name redis-5 \ -v /mydata/redis/node-5/data:/data \ -v /mydata/redis/node-5/conf/redis.conf:/etc/redis/redis.conf \ -d --net redis --ip 172.38.0.15 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf #第6个Redis容器 docker run -p 6376:6379 -p 16376:16379 --name redis-6 \ -v /mydata/redis/node-6/data:/data \ -v /mydata/redis/node-6/conf/redis.conf:/etc/redis/redis.conf \ -d --net redis --ip 172.38.0.16 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf ``` 或者通过脚本一次性启动6个Redis容器: ```java for port in $(seq 1 6); \ do docker run -p 637${port}:6379 -p 1637${port}:16379 --name redis-${port} \ -v /mydata/redis/node-${port}/data:/data \ -v /mydata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \ -d --net redis --ip 172.38.0.1${port} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf; \ done ``` 执行上述脚本,运行结果如下: ![image-20220327003633989](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327003633989-20220327004513750.png) 下面进入到redis-1容器中创建集群 ```shell #redis默认没有bash docker exec -it redis-1 /bin/sh #创建集群 redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1 ``` 查看集群信息 ```java redis-cli -c cluster info ``` ![image-20220327003726162](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327003726162.png) 查看节点信息`cluster nodes`,可以清楚的看到Redis节点的主从关系。 ![image-20220327003756927](../../Library/Application Support/typora-user-images/image-20220327003756927.png) 测试主从复制是否生效,设置一个key,可以看到我们重定向到了Redis-3节点,处理该操作的是Redis-3节点。 ![image-20220327003822952](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327003822952.png) 新建一个会话,停止Redis-3容器服务,此时重新连接Redis-cli客户端,再次获取k1,重定向到了Redis-3节点的从节点Redis-4中处理。 ![image-20220327003849192](https://uppics-1256206735.cos.ap-beijing.myqcloud.com/uppics/image-20220327003849192.png) ## Docker Compose 简介: Docker Compose 轻松高效的管理容器。定义运行多个容器。 官方介绍: 定义、运行多个容器 yaml file 配置文件 single command。命令有哪些? Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration. To learn more about all the features of Compose, see the list of features. 所有的环境都可以使用Compose Compose works in all environments: production, staging, development, testing, as well as CI workflows. You can learn more about each case in Common Use Cases. Using Compose is basically a three-step process: Define your app’s environment with a Dockerfile so it can be reproduced anywhere. Define the services that make up your app in docker-compose.yml so they can be run together in an isolated environment. Run docker compose up and the Docker compose command starts and runs your entire app. You can alternatively run docker-compose up using the docker-compose binary. 三步骤: 1、Docker保证我们的项目可以运行在任何地方 2、services什么是服务。docker-compose.yml这个文件怎么写 3、启动项目 作用:批量容器编排。 理解: compose是docker官方的开源项目。需要安装! dockerfile让程序在任何地方运行。web服务\redis\mysql\nginx…多个容器 ## Docker Swarm ## CI/CD之Jenkins