# docker-learn **Repository Path**: wh543/docker-learn ## Basic Information - **Project Name**: docker-learn - **Description**: No description available - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-04-19 - **Last Updated**: 2021-06-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # part1 Docker 概述 ## 1 容器化和虚拟化 ​ Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中, 然后发布到任何流行的Linux机器或Windows 机器上,也可以实现虚拟化。(百度百科) ### 1.1 虚拟化 ​ 在最早的时候,我们想要在线上部署一个应用。我们需要先购买服务器,然后安装操作系统及各种依赖环境,最后进行应用的部署。 问题: 1. 部署应用上线应用过程时间非常长 2. 购买服务器的花费不菲 3. 物理服务器的资源容易浪费 4. 迁移和扩展比较困难 ​ 因为有上面的问题,后来出现的虚拟化技术。 虚拟化技术就是在操作系统上多加了一个虚拟化层 (Hypervisor),可以将物理机的CPU、内存、硬盘、网络等资源进行虚拟化,再通过虚拟化出来的空间上安装操作系统,构建虚拟的应用程序执行环境。这就是我们通常说的虚拟机。 比如:VMware 、VirtualBox、KVM等。 ​ 虚拟化技术的优点: - 提升IT效率 - 降低运维成本 - 更快地部署工作负责 - 提高应用性能 - 提高服务器可用性 - 消除服务器梳理剧增情况和复杂性 ​ 虚拟机的缺点: - 占用资源较多 - 性能较差 - 扩展能力较差 - 环境迁移能力较差 ​ 于是容器化技术诞生了。 ### 1.2 容器化 ​ 容器化起源集装箱。集装箱 ——全球物流系统中一个非常重要的发明。在运输之前一次性将货物封装好到集装箱里面,之后的集装箱直接放到卡车、火车、货轮上,而且集装箱是统一标准的,自然容易机械化,因此集装箱的革命,节省了大量的资源、物流成本大大降低。 ​ 借鉴了集装箱的解决方案,提出了类似集装箱的方式封装应用以及它的运行环境,将应用和环境打包成 一个轻量化、可移植、自包含的容器。 ​ 容器是轻量级的,因为它们不需要虚拟机管理程序的额外负载,而是直接在主机的内核中运行。这意味着与使用虚拟机相比,可以在给定的硬件组合上运行更多的容器。甚至可以在实际上是虚拟机的主机中运行Docker容器。 ​ 容器有四个特点: - 容器共享宿主机内核 - 容器使用内核的功能对进程进行分组和资源限制 - 容器通过命名空间保证隔离 - 容器像是轻量级的VM(占用空间少,速度更快),但不是虚拟机 ![](.README_images/1633a35e.png) ## 2 docker 概述 ### 2.1 什么是docker - Docker开源项目背景 Docker是基于Go语言实现的开源容器项目,诞生于2013年年初,最初发起者是dotCloud公司 (Docker Inc) Docker项目已加入了Linux基金会,并遵循Apache2.0协议,全部开源代码均在https://github.com/docker/docker上进行维护。 Docker的构想是要实现“Build,Ship and Run Any App,Anywhere”,即通过对应用的封装 (Packaging)、分发(Distribution)、部署(Deployment)、运行(Runtime)生命周期进 行管理,达到应用组件“一次封装,到处运行”的目的。这里的应用组件,既可以是一个Web应 用、一个编译环境,也可以是一套数据库平台服务,甚至是一个操作系统或集群。 Docker首次为应用的开发、运行和部署提供了“一站式”的实用解决方案。 - Linux容器技术(LXC) 早期的Docker是基于Linux容器技术(Linux Containers,LXC)的。 最早的容器技术可以追溯到1982年Unix系列操作系统上的chroot工具(直到今天,主流的Unix、Linux操 作系统仍然支持和带有该工具)。早期的容器实现技术包括Sun Solaris操作系统上的 Solaris Containers(2004年发布),FreeBSD操作系统上的 FreeBSD jail(2000年左右出现), 以及GNU/Linux上的Linux-VServer和 OpenVZ。 - 从Linux容器到Docker 在LXC的基础上,Docker进一步优化了容器的使用体验 Docker提供了各种容器管理工具(如分发、版本、移植等)让用户无需关注底层的操作,可以更简单明了地管理和使用容器 Docker通过引入分层文件系统构建(aufs)和高效的镜像机制,降低了迁移难度,极大地提升了用户体验。 自0.9版本开始,Docker开发了libcontainer项目,作为更广泛的容器驱动实现,从而替换掉了LXC的实现。 ### 2.2 docker的应用场景 ​ 快速,一致地交付应用程序、镜像打包环境,避免了环境不一致的问题。Docker可以为开发人员提供标准化的本地工作环境给应用程序和服务,从而简化了开发生命周期。容器非常适合持续集成和持续交付 (CI/CD)工作流程 如果有以下场景可以使用Docker交付: - 开发人员在本地编写代码,并使用Docker容器与其他同事共享劳动成果。 - 使用Docker将应用程序推送到测试环境中,并执行自动和手动测试。 - 当开发人员发现错误时,他们可以在开发环境中对其进行修复,然后将其重新部署到测试环境中以进行测试和验证。 - 测试完成后,将修补程序推送给生产环境就像将更新的镜像推送到生产环境一样简单。 ​ **响应式部署和扩展** Docker是基于容器的平台允许高度可移植的工作负载。Docker容器可以在开发人员的本地笔记本电脑上,数据中心中的物理或虚拟机上,云提供商上或混合环境中运行。Docker的可移植性和轻量级的特性还使可以轻松地动态管理工作负载,并根据业务需求指示实时扩展或拆除应用程序和服务。 ​ **在同一个硬件上运行更多工作负载** Docker轻巧快速。它为基于虚拟机管理程序的虚拟机提供了可行,经济高效的替代方案,因此我们可以利用更多的计算能力来实现业务目标。Docker非常适合于高密度环境以及中小型部署,而需要用更少的资源做更多的事情。 ### 2.3 为什么要使用docker ​ Docker容器虚拟化的好处 ​ Docker提供了一种更为聪明的方式,通过容器来打包应用,解耦应用和运行平台。意味着迁移的时候,只需要在新的服务器上启动需要的容器就可以了,无论新旧服务器是否是同一类型的平台。 ​ Docker在开发和运维中的优势 - 更快的交付和部署 - 更高效的资源利用 - 更轻松的迁移和扩展 - 更简单的更新管理 - 提供完整的软件生态系统:镜像管理、资源隔离、文件系统、网络隔离、流程管理及服务发现 ### 2.4 docker与虚拟机比较 ​ Docker是一种轻量级的虚拟化方式。与传统虚拟机技术的特性比较如下表: | 特性 | 容器 | 虚拟机 | | -------- | ------------------ | ---------- | | 启动速度 | 秒级 | 分钟级 | | 性能 | 接近原生 | 较弱 | | 内存代价 | 很小 | 较多 | | 硬盘使用 | 一般为MB | 一般为GB | | 运行密度 | 单机支持上千个容器 | 一般几十个 | | 隔离性 | 安全隔离 | 完全隔离 | | 迁移性 | 优秀 | 一般 | ​ 传统的虚拟机方式提供的是相对封闭的隔离。Docker利用Linux系统上的多种防护技术实现了严格的隔离可靠性,并且可以整合众多安全工具。从1.3.0版本开始,Docker重点改善了容器的安全控制和镜像的安全机制,极大提高了使用Docker的安全性。 # part2 Docker快速实战 ## 1 Docker核心组件 ![](.README_images/8f0096e1.png) ### 1.1 Docker引擎 ​ Docker使用客户端-服务器架构。Docker客户端是用户与Docker交互的主要方式,与Docker守护进程(Docker引擎)进行通信。该守护进程完成了构建,运行和分发Docker容器的繁重工作。 ​ Docker客户端和守护程序可以在同一系统上运行,也可以将Docker客户端连接到远程Docker守护程序。Docker客户端和守护程序在UNIX套接字或网络接口上使用REST API进行通信。 Docker守护进程侦听Docker API请求并管理Docker对象,例如镜像,容器,网络和卷等。守护程序还可以与其他守护程序通信以管理Docker服务。 ### 1.2 Docker镜像 ​ Docker镜像类似于虚拟机镜像,可以将它理解为一个只读的模版。镜像是基于联合(Union)文件系统的一种层式的结构,由一系列指令一步步构建出来。 比如:添加一个文件、执行一个命令、打开一个端口。 ​ 镜像是创建Docker容器的基础。通过版本管理和增量的文件系统,Docker提供了一套十分简单的机制来创建和更新现有的镜像,用户可以从网上下载一个已经做好的应用镜像,并直接使用。我们可以利用Dockerfile构建自己的镜像。 ### 1.3 Docker容器 ​ 容器是一个可运行的镜像实例。可以通过Docker API或者CLI 创建、启动、停止、删除容器。容器之间都是彼此相互隔离、互不可见的。可以把容器看作是一个简易版的linux系统环境(包括root用户权限、进程空间、用户空间和网络空间等)以及运行在其中的应用程序打包而成的盒子。 ​ 镜像是Docker生命周期中的构建或者打包阶段,而容器则是启动或者执行阶段。镜像自身是只读的。容器从镜像启动的时候,会在镜像的最上层创建一个可写层。 ### 1.4 Docker仓库 ​ Docker仓库类似于代码仓库,它是Docker集中存放镜像文件的场所。 ​ 仓库注册服务器(Registry)是存放仓库的地方,其上往往存放着多个仓库 每个仓库集中存放某一类镜像,往往包 括多个镜像文件,通过不同的标签(tag)来进行区分。 ​ 根据所存储的镜像公开分享与否,Docker仓库可以分为公开仓库 (Public)和私有仓库(Private)两种形式。 ​ 目前,最大的公开仓库是 官方提供的Docker Hub,其中存放了数量庞大的镜像供用户下载。国内不少云服务提供商(如时速云、阿里云等)也提供了仓库的本地源,可以提供稳定的国内访问。Docker也支持用户在本地网络内创建一个只能自己访问的私有仓库。 ## 2 Docker安装 ​ Docker在主流的操作系统和云平台上都可以使用,包括Linux操作 系统(如Ubuntu、Debian、CentOS、Redhat等)、MacOS操作系统和 Windows操作系统,以及AWS等云平台。 ### 2.1 卸载历史版本 ```sh # 列出当前所有docker的包 yum list installed | grep docker # 卸载docker包 yum -y remove docker的包名称 # 删除docker的所有镜像和容器 rm -rf /var/lib/docker ``` ### 2.2 安装官方yum源 ```sh yum install -y yum-utils \ device-mapper-persistent-data \ lvm2 yum-config-manager \ --add-repo \ http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo ``` ### 2.3 安装Docker引擎 ```sh # 安装最新版本 # yum install -y docker-ce docker-ce-cli containerd.io # 安装指定版本 # 列出需要安装的版本列表 yum list docker-ce --showduplicates | sort -r # 安装指定版本 yum install docker-ce-18.06.1.ce # 查看版本 docker -v ``` ### 2.4 启动docker ```shell #开机启动 systemctl enable docker #启动 systemctl start docker #查看Docker状态 docker info ``` ### 2.5 添加阿里云镜像下载地址 ```sh sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://lyth1fun.mirror.aliyuncs.com"] } EOF sudo systemctl daemon-reload sudo systemctl restart docker ``` ## 3 Docker操作 ### 3.1 使用Docker镜像 #### 获取镜像 `docker pull NAME[:TAG](拉取最新的镜像)` 描述一个镜像需要包括“名称+标签”信息 例如: ```shell docker pull mysql:5.7.30 [root@localhost ~]# docker pull mysql:5.7.30 5.7.30: Pulling from library/mysql # 镜像文件一般由若干层(layer)组成 层的唯一id镜像文件一般由若干层(layer)组成 8559a31e96f4: Pull complete d51ce1c2e575: Pull complete c2344adc4858: Pull complete fcf3ceff18fc: Pull complete 16da0c38dc5b: Pull complete b905d1797e97: Pull complete 4b50d1c6b05c: Pull complete d85174a87144: Pull complete a4ad33703fa8: Pull complete f7a5433ce20d: Pull complete 3dcd2a278b4a: Pull complete ``` ​ 如果不显式指定TAG,则默认会选择latest标签,这会下载仓库中最新版本的镜像。 镜像的仓库名称中还应该添加仓库地址(即registry,注册服务器)作为前缀,默认使用的是Docker Hub服务,该前缀可以忽略 docker pull registry.hub.docker.com/mysql:5.7.30 如果从非官方的仓库下载,则需要在仓库名称前指定完整的仓库地址。 #### 查看镜像信息 `docker images` ``` ╭─liuwenhao@wenhao-MacBook-Pro in ~ ╰$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE 10.131.9.15:5000/a01-order/ydfbyc 20210416 88dbabb0706b 3 days ago 605MB tomcat 8.5.65 926c7fd4777e 9 days ago 533MB redis latest 84c5f6e03bf0 7 months ago 104MB wurstmeister/kafka latest 40094a582680 8 months ago 435MB zookeeper latest 6ad6cb039dfa 8 months ago 252MB zookeeper 3.4.14 5273b3db83ff 8 months ago 257MB nginx latest 8cf1bfb43ff5 9 months ago 132MB 10.131.9.12:5000/base/tomcat latest e430063657e3 23 months ago 900MB wurstmeister/zookeeper latest 3f43f72cb283 2 years ago 510MB sheepkiller/kafka-manager latest 4e4a8c5dabab 3 years ago 463MB postgres 8.4 f4853934b491 5 years ago 211MB ``` #### 添加镜像标签 `docker tag [原镜像名:tag号] [目标镜像名:tag号]` # 如果tag号缺省,默认latest ```sh docker tag ydfbyc:20210416 10.131.9.15:5000/i10-sjfx/ydfbyc:20210416 ``` #### 查看镜像详细信息 `docker inspect NAME[:TAG]` ```shell ╭─liuwenhao@wenhao-MacBook-Pro in ~ ╰$ docker inspect 10.131.9.15:5000/i10-sjfx/ydfbyc:20210416 [ { "Id": "sha256:88dbabb0706b1ffa261140f5bd07b55f47b8aa9d31090c8ca25569689539d638", "RepoTags": [ "10.131.9.15:5000/a01-order/ydfbyc:20210416", "10.131.9.15:5000/i10-sjfx/ydfbyc-2021041601:latest", "10.131.9.15:5000/i10-sjfx/ydfbyc:20210416", "10.131.9.15:5000/i10-sjfx/ydfbyc:latest", "ydfbyc:20210416" ], "RepoDigests": [], "Parent": "sha256:91dddb76130a85ffc0709ca911881c3ddcbf38e51197375782216dd02a19e12d", "Comment": "", "Created": "2021-04-16T13:16:46.5760885Z", "Container": "f3c97e0c8eb60b3e010613f99b5f4fcd1d23429c247f9d49c5dee4f47fd2139c", "ContainerConfig": { "Hostname": "f3c97e0c8eb6", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "ExposedPorts": { "8080/tcp": {} }, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/tomcat/bin:/usr/local/openjdk-8/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "JAVA_HOME=/usr/local/openjdk-8", "LANG=C.UTF-8", "JAVA_VERSION=8u282", "CATALINA_HOME=/usr/local/tomcat", "TOMCAT_NATIVE_LIBDIR=/usr/local/tomcat/native-jni-lib", "LD_LIBRARY_PATH=/usr/local/tomcat/native-jni-lib", "GPG_KEYS=05AB33110949707C93A279E3D3EFE6B686867BA6 07E48665A34DCAFAE522E5E6266191C37C037D42 47309207D818FFD8DCD3F83F1931D684307A10A5 541FBE7D8F78B25E055DDEE13C370389288584E7 61B832AC2F1C5A90F0F9B00A1C506407564C17A3 713DA88BE50911535FE716F5208B0AB1D63011C7 79F7026C690BAA50B92CD8B66A3AD3F4F22C4FED 9BA44C2621385CB966EBA586F72C284D731FABEE A27677289986DB50844682F8ACB77FC2E86E29AC A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 DCFD35E0BF8CA7344752DE8B6FB21E8933C60243 F3A04C595DB5B6A5F1ECA43E3B7BBB100D811BBE F7DA48BB64BCB84ECBA7EE6935CD23C10D498E23", "TOMCAT_MAJOR=8", "TOMCAT_VERSION=8.5.65", "TOMCAT_SHA512=eb5a77d75a46496f7de39c1cba5f4fc4991ec7da7717e7b37ad48b4ca2ea334aeabfd094f64977477b4b2352637b56e30e5d9acfcdf7ccd5f4269a824829dd39" ], "Cmd": [ "/bin/sh", "-c", "#(nop) ", "CMD [\"/run.sh\"]" ], "Image": "sha256:91dddb76130a85ffc0709ca911881c3ddcbf38e51197375782216dd02a19e12d", "Volumes": null, "WorkingDir": "/usr/local/tomcat", "Entrypoint": null, "OnBuild": null, "Labels": {} }, "DockerVersion": "19.03.13", "Author": "wenhao <543161890@qq.com>", "Config": { "Hostname": "", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "ExposedPorts": { "8080/tcp": {} }, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/tomcat/bin:/usr/local/openjdk-8/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "JAVA_HOME=/usr/local/openjdk-8", "LANG=C.UTF-8", "JAVA_VERSION=8u282", "CATALINA_HOME=/usr/local/tomcat", "TOMCAT_NATIVE_LIBDIR=/usr/local/tomcat/native-jni-lib", "LD_LIBRARY_PATH=/usr/local/tomcat/native-jni-lib", "GPG_KEYS=05AB33110949707C93A279E3D3EFE6B686867BA6 07E48665A34DCAFAE522E5E6266191C37C037D42 47309207D818FFD8DCD3F83F1931D684307A10A5 541FBE7D8F78B25E055DDEE13C370389288584E7 61B832AC2F1C5A90F0F9B00A1C506407564C17A3 713DA88BE50911535FE716F5208B0AB1D63011C7 79F7026C690BAA50B92CD8B66A3AD3F4F22C4FED 9BA44C2621385CB966EBA586F72C284D731FABEE A27677289986DB50844682F8ACB77FC2E86E29AC A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 DCFD35E0BF8CA7344752DE8B6FB21E8933C60243 F3A04C595DB5B6A5F1ECA43E3B7BBB100D811BBE F7DA48BB64BCB84ECBA7EE6935CD23C10D498E23", "TOMCAT_MAJOR=8", "TOMCAT_VERSION=8.5.65", "TOMCAT_SHA512=eb5a77d75a46496f7de39c1cba5f4fc4991ec7da7717e7b37ad48b4ca2ea334aeabfd094f64977477b4b2352637b56e30e5d9acfcdf7ccd5f4269a824829dd39" ], "Cmd": [ "/run.sh" ], "Image": "sha256:91dddb76130a85ffc0709ca911881c3ddcbf38e51197375782216dd02a19e12d", "Volumes": null, "WorkingDir": "/usr/local/tomcat", "Entrypoint": null, "OnBuild": null, "Labels": null }, "Architecture": "amd64", "Os": "linux", "Size": 605461003, "VirtualSize": 605461003, "GraphDriver": { "Data": { "LowerDir": "/var/lib/docker/overlay2/fcf997d350a51dcd2ee1da8c7fecb0f890cf3ee27e4b5596385dd87f64675849/diff:/var/lib/docker/overlay2/adde1b50caba9aabd6adfc611e38cdbbf6ded71ade07bf466564f61968e20920/diff:/var/lib/docker/overlay2/375d8fe024b6623ff4f2acf5ffdf910235a9a1a6e238eeb5ab7f68adb4025370/diff:/var/lib/docker/overlay2/983c5bf4953763e852b99c97d3717ae59586edc4cb3e6af53b80544af4ae0c42/diff:/var/lib/docker/overlay2/d51e52d68bd852d7cd3e6d89c68b54bdb838f189388a32a987a5bd1785daa9e8/diff:/var/lib/docker/overlay2/dd1998b3977f0ea62223114cb03c5e18bcc14145c90542ebdad4390c79b74a17/diff:/var/lib/docker/overlay2/0fdee49bc06d6b81ffa92ec2e6c371ceebe0f22af4ab1bf13702cc7882d8ecdc/diff:/var/lib/docker/overlay2/32e97927fe6b9df82b0573445af9c67299ab3a429167a8e133f039fe0a188136/diff:/var/lib/docker/overlay2/a0b21f4a235f86753ac1151491c4b0c2ca4bc9ff5c47a7cd27b7401789c97a92/diff:/var/lib/docker/overlay2/2e4c284d9741a129357b2b2478ee8fbeecb2894db51f2099f4a4611f029bfba6/diff:/var/lib/docker/overlay2/320d227ac49b7a51fa065e92f32d34e89608c5743e59036002fa91c9148f5cf2/diff:/var/lib/docker/overlay2/faacf40919e4db51ba5ac0fed9402002f471016c99a0066bc8c33fb5cf9696fa/diff:/var/lib/docker/overlay2/10fb724a91ae571300a7389e01f44a700fcfb6655d39255c51fe1766d9e8fd04/diff:/var/lib/docker/overlay2/5509e8cabb0a3fab09defaa50535f5e7855f678abff76f868f4e65df3197edea/diff:/var/lib/docker/overlay2/4589eefeb82084d754e7be6e2eb7d3a08e3afd1bc1af9f65475b3496c3aaf8cc/diff:/var/lib/docker/overlay2/2caabcfa0d4ebafadee311351960cef65acf0214a68b8f3436d4df4598b537ce/diff", "MergedDir": "/var/lib/docker/overlay2/64a1f611c2db987e211cb25fd3e4edca1723fcb74711a7155495c0f66e8f7204/merged", "UpperDir": "/var/lib/docker/overlay2/64a1f611c2db987e211cb25fd3e4edca1723fcb74711a7155495c0f66e8f7204/diff", "WorkDir": "/var/lib/docker/overlay2/64a1f611c2db987e211cb25fd3e4edca1723fcb74711a7155495c0f66e8f7204/work" }, "Name": "overlay2" }, "RootFS": { "Type": "layers", "Layers": [ "sha256:e2c6ff46235709f5178ab7c1939f4fba7237ffde84e13b1582fa5f0837c1d978", "sha256:26270c5e25fa4569f37428604f708f8863a171ec2499a066241a641017971308", "sha256:a42439ce96509df457152574716e3c07a8d5640fe9c16f5d4fb8854f72ce357a", "sha256:5d5962699bd5f016d81fc6d623cb0bc1f3853efdc48b6958053122caffc425de", "sha256:26863b4714ee050f833364c81528e499eeff4f778cde2064e2db4cb24b289f08", "sha256:30b0cc543ee7afa900638cb4c68d2ebf00e2df31c5f98ae9429aa5df3145e983", "sha256:05b4da1d24c640468d5fccbd909223e92a4449c24539aa3bc6b18087e13e7dfc", "sha256:8fefdcd19af4ab4769490e5c32c4e4da88667e54ba8f8adcc05002fcec2de5cb", "sha256:695054005ec3569aa308f0a98f70c953bdfc065ec05da26a8761b1a325bdf104", "sha256:4cae120f43f8509ffbb3ca43dcf97d0332d21caef1cde0d8276502d85c9919c9", "sha256:c89f28a4a1324f9bfb017f515c842f9778c44f0d4c2298b789db5d9fdd0f3f7a", "sha256:8158336aa8d46a4b3808b7e1711df9f83109da7b94a658c814cbf09e87f60b36", "sha256:c30be49783508920f24f317230a9784632aba43496a976d19d223a8312b11b98", "sha256:c3cbabecd1b74d1ab267ed7c243a7970e513d0c4512cf2918071f9d8cb8543cf", "sha256:a1316debb8013bc99af2072e60e853fcd45df671f304d02164c328e232c9b4c6", "sha256:21c5e1b190fffe69b5e0d5542a81d759af232f8d7ddedc08581e264fee5a1dfa", "sha256:a9bb63efbdc66695cdd0f27e64f588ddb83e2d31270cfc1469459d88f4b98c03" ] }, "Metadata": { "LastTagTime": "2021-04-16T13:43:03.0085979Z" } } ] ``` ​ 返回的是一个json格式的消息,如果我们只要求其中的一项内容,可以使用参数`-f`来指定。 ```sh ╭─liuwenhao@wenhao-MacBook-Pro in ~ ╰$ docker inspect 10.131.9.15:5000/i10-sjfx/ydfbyc:20210416 -f {{".ContainerConfig.Env"}} [PATH=/usr/local/tomcat/bin:/usr/local/openjdk-8/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin JAVA_HOME=/usr/local/openjdk-8 LANG=C.UTF-8 JAVA_VERSION=8u282 CATALINA_HOME=/usr/local/tomcat TOMCAT_NATIVE_LIBDIR=/usr/local/tomcat/native-jni-lib LD_LIBRARY_PATH=/usr/local/tomcat/native-jni-lib GPG_KEYS=05AB33110949707C93A279E3D3EFE6B686867BA6 07E48665A34DCAFAE522E5E6266191C37C037D42 47309207D818FFD8DCD3F83F1931D684307A10A5 541FBE7D8F78B25E055DDEE13C370389288584E7 61B832AC2F1C5A90F0F9B00A1C506407564C17A3 713DA88BE50911535FE716F5208B0AB1D63011C7 79F7026C690BAA50B92CD8B66A3AD3F4F22C4FED 9BA44C2621385CB966EBA586F72C284D731FABEE A27677289986DB50844682F8ACB77FC2E86E29AC A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 DCFD35E0BF8CA7344752DE8B6FB21E8933C60243 F3A04C595DB5B6A5F1ECA43E3B7BBB100D811BBE F7DA48BB64BCB84ECBA7EE6935CD23C10D498E23 TOMCAT_MAJOR=8 TOMCAT_VERSION=8.5.65 TOMCAT_SHA512=eb5a77d75a46496f7de39c1cba5f4fc4991ec7da7717e7b37ad48b4ca2ea334aeabfd094f64977477b4b2352637b56e30e5d9acfcdf7ccd5f4269a824829dd39] ``` #### 搜索镜像 `docker search 名称` ```shell ╭─liuwenhao@wenhao-MacBook-Pro in ~ ╰$ docker search mysql NAME DESCRIPTION STARS OFFICIAL AUTOMATED mysql MySQL is a widely used, open-source relation… 10768 [OK] mariadb MariaDB Server is a high performing open sou… 4056 [OK] mysql/mysql-server Optimized MySQL Server Docker images. Create… 792 [OK] percona Percona Server is a fork of the MySQL relati… 533 [OK] centos/mysql-57-centos7 MySQL 5.7 SQL database server 87 mysql/mysql-cluster Experimental MySQL Cluster Docker images. Cr… 81 centurylink/mysql Image containing mysql. Optimized to be link… 59 [OK] bitnami/mysql Bitnami MySQL Docker Image 50 [OK] ... ``` dockerhub仓库镜像, 私有仓库无法搜索到 #### 删除镜像 `docker rmi NAME[:TAG]` 当同一个镜像拥有多个标签的时候,docker rmi命令只是删除该镜像多个标签中的指定标签而已,并不影响镜像文件。当镜像只剩下一个标签的时候,使用docker rmi命令会彻底删除镜像。 使用镜像ID删除镜像 `docker rmi IMAGE ID` 如果有容器正在运行该镜像,则不能删除,如果想强行删除用`-f`(不推荐)。 #### 上传镜像 `docker push NAME[:TAG]` 上传镜像到仓库,默认上传到Docker Hub官方仓库(需要登录) ### 3.2 操作Docker容器 #### 创建容器 `docker create Name[:TAG]` 可以加选项参数 -i 交互模式 -t 伪终端 -d 后台运行 -rm 容器退出后是否自动删除 `docker create -it nginx` #### 启动容器 `docker start 容器ID` 查看容器状态 docker ps 查看运行的容器 ```sh # 查看运行中的容器 docker ps # 查看所有容器 docker ps -a ``` #### 新建并启动容器 `docker run NAME[:TAG]`相当于 `docker create + docker start` ```shell docker run -it --rm --network host tomcat:8.5.56-jdk8-openjdk ``` 可以添加选项参数 --network host 使用宿主机IP地址 man docker run docker run --help #### 终止容器 `docker stop 容器ID -t 时间(默认10s)` 首先向容器发送SIGTERM信号,等待一段超时时间(默认10s)后,再发送SIGKILL信号来终止容器 docker kill 容器ID docker kill命令会直接发送SIGKILL信号来强行终止容器 ```shell docker stop c4b2b25ff6b7 -t 5 docker kill c4b2b25ff6b7 ``` 处于终止的容器,可以docker start命令重新启动 ```sh docker start c4b2b25ff6b7 ``` docker restart 命令会将一个运行态的容器先终止,然后再重新启动 ```sh docker restart c4b2b25ff6b7 ``` #### 进入容器 docker exec -it [容器ID] /bin/bash #### 查看容器 docker inspect [容器ID] docker stats [容器ID] #### 删除容器 docker rm [容器ID] docker rm命令只能删除处于终止或退出状态的容器,并不能删除还处于运行状态的容器 要直接删除一个运行中的容器,可以添加`-f`参数。Docker会先发送SIGKILL信号给容器,终止其中的应用,之后强行删除。 ### 3.3 访问Docker仓库 Docker Hub是最大的公共镜像仓库(https://hub.docker.com/) #### 登录仓库 docker login -u username -p password 默认登陆的是docker hub ```sh ╭─liuwenhao@wenhao-MacBook-Pro in ~ ╰$ docker login -u shyshyaaron -p HaoDocker543 WARNING! Using --password via the CLI is insecure. Use --password-stdin. Login Succeeded ``` 登陆成功的用户可以上传个人制造的镜像。用户无需登录即可通过docker search 命令来查找官方仓库中的镜像,并利用docker pull命令来将它下载到本地 #### 登出仓库 docker logout 可以同时登陆多个docker仓库,因此此命令一般不执行 ```sh ╭─liuwenhao@wenhao-MacBook-Pro in ~ ╰$ docker logout Removing login credentials for https://index.docker.io/v1/ ``` #### 认证文件 Mac/Win机器上的是隐藏密码的,但是在Linux下是显示密码的,只不过进行了base64编码, 只要拷贝此文件到其他机器指定目录下(/root/.docker/config.json)即可免登录 ``` { "auths": { "https://index.docker.io/v1/": { "auth": "SGFvRG9ja2VyNTQz" }, "HttpHeaders": { "User-Agent": "Docker-Client/19.03.13 (darwin)" } } ``` ### 3.4 常用软件的容器化部署 #### mysql ```sh docker pull mysql:5.7.30 docker run --network host -e MYSQL_ROOT_PASSWORD=root -d --rm mysql:5.7.30 ``` - --network host : 宿主机IP 不能再使用端口映射 - -p 宿主机端口:容器端口 只能使用容器端口 - --rm:当容器停止后,对容器及其申请的卷执行删除操作 - -e key=value: 指定环境变量(此处指定了mysql root密码的环境变量,密码为root) - -d :后台运行 访问mysql mysql -h10.20.23.238 -uroot -proot #### tomcat ```sh docker pull tomcat:8.5.56-jdk8-openjdk docker run -it --rm --network host tomcat:8.5.56-jdk8-openjdk ``` - -it : 交互式伪客户端 - --rm:当容器停止后,对容器及其申请的卷执行删除操作 - --network host:宿主机IP 访问tomcat curl http://10.20.23.238:8080/ #### nginx ``` docker pull nginx docker run --name nginx1 --network host -d nginx ``` --name:运行的容器名称 #### redis ``` docker pull redis:5.0.9 docker run --network host -d redis:5.0.9 ``` ### 3.5 Docker命令图谱 ![](.README_images/6d8b16d7.png) ![](.README_images/79af1b3c.png) # part3 Docker核心原理 ​ 作为一种容器虚拟化技术,Docker深度应用了操作系统的多项底层支持技术,包括:Linux操作系统的命名空间(Namespace)、控制组(Control Group)、 联合文件系统(Union File System)和Linux 网络虚拟化支持。 ## 1 内部结构 ![](.README_images/dd7d8ec2.png) Docker Engine是具有以下主要组件的客户端-服务器应用程序: - 服务器是一种长期运行的程序,称为守护程序进程(docker命令) - Rest API,它指定程序可以用来与守护程序进行通信并指示其操作的接口。 - 命令行界面(CLI)客户端(docker命令) ​ 这么做的好处就是可以通过RestAPI将客户端和服务端解耦,这样服务端就可以做到Volume/Network等做成插件化组件。 ## 2 容器声明周期 ### 状态与命令对应表 | 状态 | 命令 | | ----------------- | -------------------------------------------------------- | | created:初创建 | docker create | | running:运行状态 | docker run、docker start、docker restart、docker unpause | | stopped:停止状态 | docker stop、docker kill | | paused:暂停状态 | docker pause | | deleted:删除状态 | docker rm | ### 状态转化图 ![](.README_images/a4e6bb6c.png) 需要根据实际情况选择的操作 - killed by out-of-memory(因内存不足被终止) - 宿主机内存被耗尽,也被称为OOM:非计划终止 - 这时需要杀死最吃内存的容器 - 然后进行选择操作 - container process exit(异常终止) - 出现容器被终止后,将进入should restart选择操作: - yes 需要重启,容器执行start命令,转为运行状态 - no 不需要重启,容器转为停止状态 ## 3 容器命名空间 ​ 命名空间(namespace)是Linux内核的一个强大特性,为容器虚拟化的实现带来极大便利。 ​ 命名空间要对内存、CPU、网络IO、硬盘IO、存储空间、文件系统、网络、PID、UID、IPC等的相互隔离。 ### 3.1 进程命名空间(PID namespace) ​ PID namespace 对集成PID重新编号,即两个不同的namespace下的进程可以用相同的PID。 ​ 内核为所有的PID namespace维护了一个树状结构,最顶层的是系统初始时创建的,被称作root namespace(进程号1)。 ​ 它创建的PID namespace被称作为child namespace(树的字节点), ​ 不同的PID namespace会形成一个层级体系,所属的父节点可以看到子节点中的进程,并通过信号等方式对子节点中的进程产生影响。反过来,子节点却看不到父节点PID namespace的任何内容。 ### 3.2 网络命名空间(network namespace) ​ 通过网络命名空间,可以实现网络隔离。 ​ 网络命名空间为进程提供了一个完全独立的网络协议栈的视图,包括网络设备接口、IPv4和IPv6 协议 栈、IP路由表、防火墙规则、sockets等,这样每个容器的网络就能隔离开来。 ​ Docker采用虚拟网络设备(Virtual Network Device)的方式,将不同命名空间的网络设备连接到一 起。 ​ 默认情况下,容器中的虚拟网卡将同本地主机上的docker0网桥连接在一起。 ![](.README_images/ec3a2cad.png) ### 3.3 IPC命名空间(IPC namespace) ​ 进程间通信(Inter-Process Communitication, IPC)涉及的IPC资源包括常见的信号量,消息队列和共享内存。 ​ 申请IPC资源就申请了一个全局唯一的32位ID,所以IPC namespace中实际上包含了系统IPC标识符以及实现POSIX消息队列的文件系统。 ​ PID Namespace和IPC Namespace可以组合起来一起使用,在同一个IPC namespace下的进程彼此可见,不同IPC namespace下的进程则互相不可见。 ### 3.4 挂载命名空间(Mount namespace) ​ 类似于chroot,将一个进程放到一个特定的目录执行。 ​ 挂载命名空间允许不同命名空间的进程看到的文件结构不同,这样每个命名空间中的进程所看到的文件目录彼此被隔离。 ​ Mount namespace通过隔离文件系统挂载点对隔离文件系统通过提供支持,是Linux支持的第一个 namespace。隔离后不同mount namespace中的文件结构发生变化也不互相影响,可以通过 /proc/[pid]/mounts 查看到所有挂载在当前namespace中的文件系统,还可以通过 /proc/[pid]/mountstats 查到mount namespace中文件设备的统计信息,包括挂载文件的名字、 文件系统、挂载位置等。 ### 3.5 UTS命名空间(UTS namespace) ​ UTS(UNIX Time-sharing System)命名空间允许每个容器拥有独立的主机名和域名,从而可以虚拟出一个有独立主机名和网络空间的环境,就跟网络上一台独立的主机一样。 ​ 这样每个Docker容器就可以拥有独立的主机名和容器了,在网络上可以被视作一个独立的节点,而非宿主机上的一个进程 ​ Docker中,每个镜像基本上都以自身提供的服务名来命名镜像的hostname,而且不会对宿主机产生任何 影响,其原理就是利用了UTS namespace ​ 默认情况下,Docker容器的主机名就是返回的容器ID。 ### 3.6 用户命名空间(user namespace) ​ 每个容器可以有不同的用户和组id,也就是说可以在容器内使用特定的内部用户执行程序,而非本地系统上存在的用户。 每个容器内部都可以有root帐号,但跟宿主主机不在一个命名空间。 通过使用隔离的用户命名空间可以提高安全性,避免容器内进程获取到额外的权限。 ## 4 资源配额限制(CGroups) ​ 控制组(CGroups)是Linux内核的一个特性,主要用来对共享资源进行隔离、限制、审计等。只有能控制分配到容器的资源,才能避免多个容器同时运行时对宿主机系统的资源竞争。控制组可以提供对容器的内存、CPU、磁盘IO等资源进行限制和管理。 ​ 默认情况下,容器没有资源限制,并且可以使用主机内核调度程序允许的尽可能多的给定资源, 这可以在我们查看容器状态时看到 `docker stats 容器ID` 。Docker提供了一些方法来控制容器可以使用多少内存或CPU,在设置 docker run 命令的运行时配置标志。 ### 4.1 CPU | 选项 | 描述 | | ---------------------- | ------------------------------------------------------------ | | `--cpus=` | 指定一个容器可以使用多少可用的CPU资源。例如,如果主机有两个CPU,并 且您设置了` --cpus="1.5"` ,则可以保证容器最多容纳一半的CPU。这相当于设置 `--cpu-period="100000"` 和 `--cpu-quota="150000"` 。在Docker 1.13及更高 版本中可用。 | | `--cpu-period=` | 指定CPU CFS调度程序周期,该周期与一起使用 `--cpu-quota` 。默认为100微秒。大多数用户不会更改默认设置。如果您使用Docker 1.13或更高版本,请改 用 --cpus 。 | | `--cpu-quota=` | 在容器上设置CPU CFS配额。`--cpu-period`节流之前,容器被限制为每个微秒数。这样就充当了有效的上限。如果您使用Docker 1.13或更高版本,请改用`--cpus`。 | | `--cpuset-cpus` | 限制容器可以使用的特定CPU或内核。如果一个或多个CPU,则容器可以使用逗号分隔的列表或以连字符分隔的CPU范围。第一个CPU编号为0。有效值可能是 0-3 (使用第一,第二,第三和第四CPU)或 1,3 (使用第二和第四CPU)。 | | `--cpu-shares` | 将此标志设置为大于或小于默认值1024的值,以增加或减少容器的重量,并使其可以访问更多或更少比例的主机CPU周期。仅在限制CPU周期时才执行此操作。当有足够的CPU周期可用时,所有容器都会根据需要使用尽可能多的 CPU。这样,这是一个软限制。 `--cpu-shares` 不会阻止以群集模式调度容器。 它将容器CPU资源的优先级分配给可用的CPU周期。它不保证或保留任何特定 的CPU访问权限。 | ```sh docker run --cpus 1.5 --rm -d --network host nginx:latest ``` ### 4.2 内存 | 选项 | 描述 | | ---------------------- | ------------------------------------------------------------ | | `-m`,`--memory=` | 容器可以使用的最大内存量。如果设置此选项,则最小允许值为`4m` (4 MB)。 | | `--memory-swap` | 允许此容器交换到磁盘的内存量。 | | `--memory-swappiness` | 默认情况下,主机内核可以换出一定比例的容器使用的匿名页面。您可以设置 `--memory-swappiness` 一个介于0到100之间的值来调整此百分比。 | | `--memory-reservation` | 允许您指定一个小于` --memory` Docker在主机上检测到争用或内存不足时激活的软限制。如果使用 `--memory-reservation` ,则必须将其设置为低于` --memory` 以使其具有优先权。因为这是一个软限制,所以不能保证容器不超过该限制 | | `--kernel-memory` | 容器可以使用的最大内核内存量。允许的最小值是 `4m` 。由于无法交换内核内存,因此内核内存不足的容器可能会阻塞主机资源,这可能会对 主机和其他容器产生副作用 | | `--oom-kill-disable` | 默认情况下,如果发生内存不足(OOM)错误,则内核将终止容器中的进程。要更改此行为,请使用该 `--oom-kill-disable` 选项。仅在还设置了该 `-m/--memory` 选项的容器上禁用OOM杀手。如果 `-m` 未设置该标志,则主机可能会用完内存,内核可能需要终止主机系统的进程以释放内存。 | ​ 大部分的选项取正整数,跟着一个后缀 b,k,m,g ,表示字节,千字节,兆字节或千兆字节。 ```sh [root@localhost ~]# docker run --rm -d --network host -m 100m nginx:latest 6491a0d526a44241c744af0bf9a43d5ba80705d053cc446f051857193db40632 ``` ### 4.3 磁盘读写 ​ Block I0指的是磁盘的读写,docker可通过设置权重限制bps和iops的方式控制容器读写磁盘的带宽。 ```shell docker run -it --device-write-bps /dev/sda:30MB ubuntu ``` ## 5 联合文件系统(UnoinFS) ​ 联合文件系统(UnionFS)是一种轻量级的高性能分层文件系统,它支持将文件系统中的修改信息作为一次提交,并层层叠加,同时可以将不同目录挂载到同一个虚拟文件系统下,应用看到的是挂载的最终结果。 ​ UnionFS可以把文件系统上多个目录(也叫分支)内容联合挂载到同一个目录下,而目录的物理位置是分开的。 ​ 联合文件系统是实现Docker镜像的技术基础。Docker镜像可以通过分层来进行继承。 ### 5.1 Docker存储 ​ Docker目前通过插件化方式支持多种文件系统后端。 ​ Debian/Ubuntu 上成熟的AUFS(Another Union File System,或v2版本往后的Advanced Multilayered Unification File System),就是一种联合文件系统实现 AUFS支持为每一个成员目录(类似Git的分支)设定只读(readonly)、读写(readwrite)或写出(whiteout-able)权限。 ​ 同时 AUFS里有一个类似分层的概念,对只读权限的分支可以在逻辑上进行增量地修改(不影响只读部分的) 。 ​ Docker镜像自身就是由多个文件层组成,每一层有唯一的编号(层ID)。 ​ 当Docker利用镜像启动一个容器时,将在镜像文件系统的最顶端再挂载一个新的可读写的层给容器。容器中的内容更新将会发生在可读写层。 ![](.README_images/39ea243e.png) ​ 通过docker history查看一个镜像由哪些层组成: ``` docker history nignx:latest ``` ### 5.2 多种文件系统比较 ​ Docker目前支持的联合文件系统种类包括AUFS、OverlayFS、 btrfs、vfs、zfs和Device Mapper等,如下表: | 文件系统 | 说明 | | ------------------------------------------------------------ | ------------------------------------------------------------ | | AUFS(Another Union File System、的 Advanced Multilayered Unification File System) | 最早支持的文件系统,对Debian/Ubuntu支持 好,虽然没有合并到Linux内核中,但成熟度很高 | | OverlayFS | 类似于AUFS,性能更好一些,已经合并到内核, 未来会取代AUFS,但成熟度有待提高 | | Device Mapper | Redhat公司和Docker团队一起开发用于支持 RHEL的文件系统,内核支持,性能略慢,成熟度高 | | btrfs | 参考zfs等特性设计的文件系统,由Linux社区开发,试图未 来取代Device Mapper,成熟度有待提高 | | vfs | 基于普通文件系统(ext、nfs等)的中间层抽象,性能差,比较占用空间,成熟度也一般 | | zfs | 最初设计为Solaris 10上的写时文件系统,拥有不 少好的特性,但对Linux支持还不够成熟 | ​ 通过docker info的Storage Driver可以看到 ## 6 容器网络 ​ Docker的本地网络实现其实就是利用了Linux上的网络命名空间和虚拟网络设备(veth pair) 。要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)与外界相通,并可以收发数据包,如果不同子网之间要进行通信,需要额外的路由机制。 ​ Docker中的网络接口默认都是虚拟的接口。Docker容器网络在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通(这样的一对接口叫做veth pair) ![](.README_images/9bf9a0b3.png) Docker的网络定义: - bridge:默认值,在Docker网桥docker0上为容器创建新的网络栈。 - host:对于独立容器,不要将容器网络放到隔离的命名空间中,然后直接使用宿主机的网络(宿主机IP)。`host`仅可用于Docker 17.06以及更高版本上的集群服务。 - overlay:覆盖网络将多个Docker守护程序连接在一起,并使集群服务能够相互通信。还可以使用覆盖网络来促进集群服务和独立容器之间或不同docker守护程序上的两个独立容器之间的通信。这种策略消除了在这些容器之间进行操作系统级路由的需要。 - macvlan:Macvlan网络允许您将MAC地址分配给容器,使其在网络上显示为物理设备。Docker 守护程序通过其MAC地址将流量路由到容器。macvlan 在处理希望直接连接到物理网络而不是通过Docker主机的网络堆栈进行路由的旧应用程序时,使用驱动程序有时是最佳选择。 - none:对于此容器,禁用所有联网。通常与自定义网络驱动程序(user_defined_network)一起 使用。 - 网络插件:可以在Docker上安装和使用第三方网络插件。 查看已经安装的驱动 ``` docker network ls ``` 使用默认网络 ```shell docker run -rm -d --name my_nginx nginx ``` 使用宿主机网络 ```sh docker run -rm -d --network host --name my_nginx nginx ``` 使用网络网卡模式 ```sh # 创建网段 docker network create --subnet=172.18.0.0/24 net1 # 查看网络 docker network ls # 启动容器时指定网络 docker run -it --rm -d --network net1 nginx # 删除网络 docker network rm net1 ``` ## 7 存储卷 ​ 默认情况下,在容器内创建的所有文件都存储在可写容器层上。这样做问题有: - 当该容器不再存在时,数据将不会持久保存,如果另一个进程需要它,则可能很难从容器中取出数据。 - 容器的可写层与运行容器的主机紧密耦合。不能轻易地将数据移动到其他地方。 - 写入容器的可写层需要存储驱动程序来管理文件系统。性能比直接写入主机要低。 ​ Docker为容器提供了两个选项来将文件存储在主机中,以便即使容器停止后文件也可以持久存储: - 数据卷(Data Volumes):容器内数据直接映射到本地主机环境 - 数据卷容器(Data Volume Containers):使用特定容器维护数据卷,也成为bind mounts(绑定挂载) ​ 如果在Linux上运行Docker,则还可以使用 tmpfs 挂载。如果在Windows上运行Docker,则还可以使用命名管道 ### 7.1 数据卷(Data Volumes) ​ 数据卷是一个可供容器使用的特殊目录,它将主机操作系统目录直接映射进容器,类似于Linux的mount操作。具备以下特性: - 可以在容器之间共享和重用 - 对数据卷数据的修改会立马生效 - 对数据卷的更新不会影响镜像 - 卷会一直存在,直到没有容器使用,可以安全地卸载它 #### 管理数据卷 ```sh # 显示创建卷 docker volume create volumename # 在容器内创建卷 docker run -v volumename:/dockerpath ... # 数据卷清单查询 docker volume ls # 数据卷详情查询 docker volume inspect volumename # 删除卷 docker volume rm volumename ``` #### 启动一个volume的容器 ​ 如果使用尚不存在的卷启动容器,则docker将创建该卷。以下示例将myvol2挂载到容器的/app目录。 ​ 下面的-v和--mount示例产生相同的结果。不能同时运行它们,除非在运行第一个devtest容器和myvol2卷之后将其删除。 ``` docker run -d \ --name devtest \ -v myvol2:/app \ nginx:latest ``` 使用docker inspect devtest验证创建卷并安装正确。查找Mounts部分。 ``` docker inspect devtest ``` ### 7.2 数据卷容器(Data Volume Containers) ​ 数据卷容器也称为绑定挂载,使用绑定安装时,主机上的文件或目录将安装到容器中。该文件或目录不需要在Docker主机上已经存在。如果尚不存在,则按需创建。 ​ 可以通过容器中运行的进程来更改主机文件系统,包括创建,修改或删除重要的系统文件或目录。 #### 使用场景 - 多个容器之间共享一些持续更新的数据 - Docker主机上的开发环境和容器之间共享源代码 #### 创建和管理数据卷容器 创建容器 ```shell docker run -d \ -it \ --name devtest \ -v "${pwd}"/target:/app \ nginx:latest ``` 使用docker inspect devtest验证绑定安装正确创建。查找Mounts部分。 使用只读绑定安装 ​ 对于某些开发应用程序,容器需要写入绑定安装,因此更改将传播回Docker主机。在其他时间,容器仅需要读取访问权限。 ​ 此示例修改了上面的示例,但ro通过在容器中的安装点之后添加到(默认为空)选项列表中,将目录作 为只读绑定安装进行安装。如果有多个选项,请用逗号分隔。 ``` docker run -d \ -it \ --name devtest \ -v "${pwd}"/target:/app:ro \ nginx:latest ``` ### 7.3 tmpfs挂载 ​ tmpfs挂载仅存储在主机系统的内存中,不会写入主机系统的文件系统中,不会持久化在磁盘上,无论是在docker主机上还是在容器内。可以使用它来存储非持久状态或敏感信息。 ​ 要在容器中使用 tmpfs ,请使用 --tmpfs 标志,或将 --mount 标志与type=tmpfs 和 destination 选项一起使用。没有 source 了 tmpfs 标志。以下示例在Nginx容器中的上创建一个 tmpfs 安装 /app。 ``` docker run -d \ -it \ --name tmptest \ --tmpfs /app \ nginx:latest ``` 指定tmpfs选项 ​ tmpfs安装允许两个配置选项,都不是必需的。如果需要指定这些选项,则必须使用--mount标志,因 为该--tmpfs标志不支持它们。 tmpfs-size : tmpfs安装的大小(以字节为单位)。默认情况下不受限制。 tmpfs-mode: tmpfs的文件模式(八进制)。例如1770 (不是全局可读) ```shell docker run -d \ -it \ --name tmptest \ --mount type=tmpfs,destination=/app,tmpfs-mode=1770 \ nginx:latest ``` # part4 Docker高级实战 ## 1 DockerFile ### Dockerfile简介 Dockerfile其实就是我们用来构建Docker镜像的源码,当然这不是所谓的编程源码,而是一些命令的组合,只要理解它的逻辑和语法格式,就可以编写Dockerfile了。 简单来说,Dockerfile的作用:它可以让用户个性化定制Docker镜像,因为工作环境中的需要各式各样,网络上的镜像很难满足实际的需求。 ### Dockerfile常见命令 | 命令 | 作用 | | -------------------------------- | ------------------------------------------------------------ | | FROM image_name:tag | 指定拉取哪个基础镜像 | | MAINTAINER user_name | 声明镜像的作者 | | ENV key value | 设置环境变量(可以写多个) | | RUN command | 编译镜像是运行的脚本(可以写多个) | | CMD | 设置容器的启动命令 | | ENTRYPOINT | 设置容器的入口程序 | | ADD source_dir/filedest_dir/file | 将宿主机的文件复制到容器内,如果是一个压缩文件,将会在复制后自动解压(ADD application.yml /etc/project/) | | COPY src dest | src:为Dockerfile所在目录的相对路径、文件或目录。dest:镜像中的目标路径,相对和绝对都可以。和ADD相似,但是如果有压缩文件并不能解压(COPY run.sh /usr/local/bin/) | | WORKDIR path_dir | 设置工作目录,之后的命令都基于此工作目录,若不存在则创建 | | ARG | 设置编译镜像时加入的参数 | | VOLUME | 设置容器的挂载卷 | | EXPOSE | 声明镜像内服务所监听的端口。无论`EXPOSE`设置如何,都可以在运行时使用该`-p`标志覆盖它们。例如 docker run -p 80:80/tcp | RUN、CMD、ENTRYPOINT的区别 RUN:用于指定docker build过程中要运行的命令,即是创建Docker镜像(image)的步骤。 CMD:设置容器的启动命令,Dockerfile中只能有一条CMD命令,如果写了多条则最后一条生效,CMD不支持接收docker run的参数。 ENTRYPOINT:入口程序是容器启动时执行的程序,docker run 中最后的命令作为参数传递给入口程序,ENTRYPOINT类似于CMD指令,但是可以接收 docker run 的参数。 mysql官方镜像的Dockerfile内容如下: ```sh FROM debian:jessie # add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added RUN groupadd -r mysql && useradd -r -g mysql mysql RUN mkdir /docker-entrypoint-initdb.d # FATAL ERROR: please install the following Perl modules before executing /usr/local/mysql/scripts/mysql_install_db: # File::Basename # File::Copy # Sys::Hostname # Data::Dumper RUN apt-get update && apt-get install -y perl --no-install-recommends && rm -rf /var/lib/apt/lists/* # gpg: key 5072E1F5: public key "MySQL Release Engineering " imported RUN apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys A4A9406876FCBD3C456770C88C718D3B5072E1F5 ENV MYSQL_MAJOR 5.6 ENV MYSQL_VERSION 5.6.26 RUN echo "deb http://repo.mysql.com/apt/debian/ jessie mysql-${MYSQL_MAJOR}" > /etc/apt/sources.list.d/mysql.list # the "/var/lib/mysql" stuff here is because the mysql-server postinst doesn't have an explicit way to disable the mysql_install_db codepath besides having a database already "configured" (ie, stuff in /var/lib/mysql/mysql) # also, we set debconf keys to make APT a little quieter RUN { \ echo mysql-community-server mysql-community-server/data-dir select ''; \ echo mysql-community-server mysql-community-server/root-pass password ''; \ echo mysql-community-server mysql-community-server/re-root-pass password ''; \ echo mysql-community-server mysql-community-server/remove-test-db select false; \ } | debconf-set-selections \ && apt-get update && apt-get install -y mysql-server="${MYSQL_VERSION}"* && rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/mysql && mkdir -p /var/lib/mysql # comment out a few problematic configuration values # don't reverse lookup hostnames, they are usually another container RUN sed -Ei 's/^(bind-address|log)/#&/' /etc/mysql/my.cnf \ && echo 'skip-host-cache\nskip-name-resolve' | awk '{ print } $1 == "[mysqld]" && c == 0 { c = 1; system("cat") }' /etc/mysql/my.cnf > /tmp/my.cnf \ && mv /tmp/my.cnf /etc/mysql/my.cnf VOLUME /var/lib/mysql COPY docker-entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] EXPOSE 3306 CMD ["mysqld"] ``` #### 使用Dockerfile制作镜像 1)上传jar包到服务器 2)编写Dockerfile ```sh FROM openjdk:8-jdk-alpine ARG JAR_FILE COPY ${JAR_FILE} app.jar EXPOSE 8083 ENTRYPOINT ["java","-jar","/app.jar"] ``` 3)构建镜像 ```sh # . : 根据当前上下文环境构建 docker build --build-arg JAR_FILE=RuoYi.jar -t ruoyi:v1 . ``` ​ 说明:`docker build` 最后的 `. `号,其实是在指定镜像构建过程中的上下文环境的目录。当构建的时候,用户会指定构建镜像上下文的路径, `docker build` 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。`COPY RuoYi.jar app.jar`这一动作并不是要复制执行`docker build` 命令所在的目录下的 `RuoYi.jar` ,也不是复制 Dockerfile 所在目录下的 `RuoYi.jar` ,而是复制上下文`(context)` 目录下的 `RuoYi.jar` 。 执行日志如下: ```sh [root@wenhao target]# pwd /var/lib/jenkins/workspace/web_demo_pipeline/target [root@wenhao target]# vi Dockerfile [root@wenhao target]# ls classes Dockerfile generated-sources maven-archiver maven-status RuoYi RuoYi.war RuoYi.war.original [root@wenhao target]# docker build --build-arg JAR_FILE=RuoYi.jar -t ruoyi:v1 . Sending build context to Docker daemon 79.96MB Step 1/5 : FROM openjdk:8-jdk-alpine ---> a3562aa0b991 Step 2/5 : ARG JAR_FILE ---> Running in 2a2ccf95a9fe Removing intermediate container 2a2ccf95a9fe ---> fa67bba42d3f Step 3/5 : COPY ${JAR_FILE} app.jar ---> be11348cdaeb Step 4/5 : EXPOSE 8083 ---> Running in 9f94665c2421 Removing intermediate container 9f94665c2421 ---> 8004381001a5 Step 5/5 : ENTRYPOINT ["java","-jar","/app.jar"] ---> Running in 7eb95197d48d Removing intermediate container 7eb95197d48d ---> ee7e5bff685b Successfully built ee7e5bff685b Successfully tagged ruoyi:v1 ``` 4)检查镜像是否创建成功 ![](.README_images/aa6c2ad9.png) 5)创建容器 ``` docker run -it -d --name=ruoyi -p 8081:80 ruoyi:v1 ``` 查看容器日志 docker logs -f containid ``` [root@wenhao target]# docker run -it -d --name ruoyi -p 8081:80 ruoyi:v1 62a5e09baddfa637ee10cd5e5416514c7430675de8747bcd2b7ecfa89b8ff0bb [root@wenhao target]# docker logs -f 62a5e09baddfa637ee10cd5e5416514c7430675de8747bcd2b7ecfa89b8ff0bb ``` ### Dockerfile模版 #### CentOS镜像模版 ```sh # 依据哪个镜像创建 From centos:7.6.1810 # 指定容器内部使用语言 ENV LANG="en_US.UTF-8" ENV LC_ALL="en_US.UTF-8" # 使用亚洲/上海时区 RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime # 创建工作目录 RUN mkdir -p /data/apps /data/data /data/logs ; ln -s /data/apps /apps # 安装字体 RUN yum install -y epel-release RUN yum groupinstall -y "fonts" RUN yum install -y kde-l10n-Chinese # 安装openssl等依赖包 RUN yum install -y openssl openssl-devel RUN yum install -y crontabs cronolog ntp # 安装数据库依赖 RUN yum install -y mariadb-libs RUN ln -s /usr/lib64/mysql/libmysqlclient.so.18 /usr/lib64/libmysqlclient_r.so.16 RUN yum install -y gcc cmake RUN yum install -y lrzsz telnet net-tools file bind-utils less RUN yum install -y jq xml2; yum clean all RUN yum install -y expat-devel apr-devel ghostscript ghostscript-devel # 运行容器时的默认命令 CMD ["/bin/bash"] ``` 构建命令: ```sh docker build -f dockerfile-centos -t centos:7.6 . docker run --rm -it centos:7.6 ``` #### JDK镜像模版 ``` FROM centos:7.6 ENV JAVA_HOME="/apps/jdk" ENV PATH=${JAVA_HOME}/bin:$PATH ADD ./jdk-8u251-linux-x64.tar.gz /apps/ RUN ln -s /apps/jdk1.8.0_251 /apps/jdk ADD ./UnlimitedJCEPolicyJDK8/US_export_policy.jar /apps/jdk/jre/lib/security/ ADD ./UnlimitedJCEPolicyJDK8/local_policy.jar /apps/jdk/jre/lib/security/ ADD ./msyhbd.ttf /apps/jdk/jre/lib/fonts/ ADD ./msyh.ttf /apps/jdk/jre/lib/fonts/ CMD ["/bin/bash"] ``` 构建命令: ```sh #下载jce_policy-8.zip并解压到当前目录 wget http://pkgs-linux.cvimer.com/jdk/1.8/jce_policy-8.zip unzip jce_policy-8.zip #下载jdk-8u251-linux-x64.tar.gz、msyh.ttf、msyhbd.ttf wget http://pkgs-linux.cvimer.com/jdk/1.8/jdk-8u251-linux-x64.tar.gz wget http://pkgs-linux.cvimer.com/fonts/msyh.ttf wget http://pkgs-linux.cvimer.com/fonts/msyhbd.ttf #构建 docker build -f dockerfile-jdk -t centos7.6/jdk1.8 . ``` # part5 Docker运维管理 ## 1 Swarm 集群管理 ​ docker swarm是docker官方提供的一套容器编排系统,是Docker公司推出的官方容器集群平台。基于Go语言实现。它的架构如下: ![](.README_images/dd724fbd.png) ### 1.1 Swarm的核心概念 #### 1.1.1 集群 ​ 一个集群由多个Docker主机组成,这些Docker主机以**集群模式**运行,并充当Manager(用于管理成员资格和委派)和Worker(运行集群服务)。 ​ 与独立容器相比,集群服务的主要优势之一是,可以修改服务的配置,包括它所连接的网络和卷,而无需手动重新启动服务。 ​ 独立容器和集群服务之间的主要区别在于,只有集群管理器可以管理集群,而独立容器可以在任何守护程序上启动。 #### 1.1.2 节点 ​ swarm是一系列节点的集合,而节点可以是一台裸机或者一台虚拟机。一个节点能扮演一个或者两个角色,manager或者worker。 ##### manager节点 ​ Docker Swarm集群需要至少一个manager节点,节点之间使用Raft consensus protocol进行协同工作。通常,第一个启动docker swarm的节点将成为leader,后来加入的都是follower。当前的leader如果挂掉,剩余的节点将重新选举出一个新的leader。每一个manager都有一个完整的当前集群状态的副本,可以保证manager的高可用。 ##### worker节点 ​ worker节点是运行实际应用服务的容器所在的地方。理论上,一个manager节点也能同时成为worker节点,但在生产环境中,不建议这么做。woker节点之间,通过control plane进行通信,这种通信使用gossip协议,并且是异步的。 #### 1.1.3 服务和任务 ​ 集群中的stacks,services,tasks的关系,如下图: ![](.README_images/4acc6329.png) ##### services(服务) ​ swarm service是一个抽象的概念,它只是一个对运行在swarm集群上的应用服务,所期望状态的描述。它就像一个描述了下面物品的清单列表一样: - 服务名称 - 使用哪个镜像来创建容器 - 要运行多少个副本 - 服务的容器要连接到哪个网络上 - 应该映射哪些端口 ##### task(任务) ​ 在Docker Swarm中,task是一个部署的最小单元,task与容器是一对一的关系。 ##### stack(栈) ​ stack是描述一系列相关services的集合。我们通过在一个yaml文件中来定义一个stack。 #### 1.1.4 负载均衡 ​ 集群管理器使用入口负载均衡将要从集群外部获取的服务公开给集群。集群管理器可以自动为服务分配一个已发布端口,也可以为该服务配置一个已发布端口。可以指定任务未使用的端口。如果未指定端口,则集群管理器会为服务分配`30000-32767`范围内的端口。 ​ 集群模式具有一个内部DNS组件,该组件自动为集群中的每个服务分配一个DNS目录。集群管理器使用内部负载均衡根据服务的DNS名称在集群内的服务之间分配请求。 ### 1.2 Swarm安装 #### 1.2.1 环境准备 > (1)根据Vagrantfile创建3台centos机器 新建swarm-docker-centos7文件夹,创建Vagrantfile。 这里采用的网络类型为private_network,https://www.vagrantup.com/docs/networking/private_network ```sh # -*- mode: ruby -*- # vi: set ft=ruby : # 这里设置的ip得跟virtulbox所在网段一致 Vagrant.require_version ">= 1.6.0" boxes = [ { :name => "manager-node", :eth1 => "192.168.56.101", :mem => "1024", :cpu => "1" }, { :name => "worker01-node", :eth1 => "192.168.56.102", :mem => "1024", :cpu => "1" }, { :name => "worker02-node", :eth1 => "192.168.56.103", :mem => "1024", :cpu => "1" } ] Vagrant.configure(2) do |config| config.vm.box = "centos/7" boxes.each do |opts| config.vm.define opts[:name] do |config| config.vm.hostname = opts[:name] config.vm.provider "vmware_fusion" do |v| v.vmx["memsize"] = opts[:mem] v.vmx["numvcpus"] = opts[:cpu] end config.vm.provider "virtualbox" do |v| v.customize ["modifyvm", :id, "--memory", opts[:mem]] v.customize ["modifyvm", :id, "--cpus", opts[:cpu]] v.customize ["modifyvm", :id, "--name", opts[:name]] end config.vm.network :private_network, ip: opts[:eth1] end end config.vm.synced_folder "./labs", "/home/vagrant/labs" config.vm.provision "shell", privileged: true, path: "./setup.sh" end ``` setup.sh ```sh #/bin/sh # install some tools sudo yum install -y vim gcc glibc-static telnet bridge-utils net-tools # install docker curl -fsSL get.docker.com -o get-docker.sh sh get-docker.sh # start docker service sudo systemctl start docker rm -rf get-docker.sh ``` > (2)进入对应的centos里面,使得root账户能够登陆,从而使用XShell登陆。 ```shell vagrant ssh manager-node/worker01-node/worker02-node sudo -i vi /etc/ssh/sshd_config # 修改PasswordAuthentication yes passwd 修改密码 systemctl restart sshd # vagrant ssh-config 可以查看各节点的ip和端口以及私钥的本地路径。可以直接通过私钥连接,也可以使用密码连接(经过上述操作) ``` #### 1.2.2 搭建Swarm集群 > (1)进入manager(manager node也可以作为worker node提供服务) ``` docker swarm init --advertise-addr=192.168.56.100 ``` 注意输出日志,拿到worker node加入manager node的信息(如果忘记了token,则在manager节点执行`docker swarm join-token manager`)。 ```shell [root@docker-node1 ~]# docker swarm init --advertise-addr=192.168.56.102 Swarm initialized: current node (antj7sc3p3yfgbuas3io0k54x) is now a manager. To add a worker to this swarm, run the following command: docker swarm join --token SWMTKN-1-07nmx8bsb8yocwyr5yqcl1ug7vfflhnie9pv0xbljwlls8x23d-ek6yqm0iwe3gtezpmar98bowr 192.168.56.100:2377 To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions. # 将节点强制驱除集群 docker swarm leave --force ``` > (2)进入两个worker,并执行上述提示要执行的脚本 ```sh docker swarm join --token SWMTKN-1-07nmx8bsb8yocwyr5yqcl1ug7vfflhnie9pv0xbljwlls8x23d-ek6yqm0iwe3gtezpmar98bowr 192.168.56.102:2377 ``` > (3)进入到manager node查看集群状态 ```sh docker node ls ``` ```sh [root@docker-node3 ~]# docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION ibwnxbusonmw4684xlp14qxoz worker01-node Ready Active 20.10.6 xyx5yxf7setlvuvfz1ppycmz9 worker02-node Ready Active 20.10.6 gs1ytw7c8ew6c0jienlplmazm * manager-node Ready Active Leader 20.10.6 ``` > (4)node类型的转化 > > 可以将worker提升成manager,从而保证manager的高可用 ```shell docker node promote worker01-node docker node promote worker02-node # 降级可以用demote docker node demote worker01-node ``` ### 1.3 Swarm基本操作 #### 1.3.1 Service > (1)创建一个service(https://docs.docker.com/engine/reference/commandline/service_create/) ```sh [root@docker-node3 ~]# docker service create --name my_nginx -p 8081:80/tcp --replicas 2 nginx ipiren07pmgkqv56pombhkao7 overall progress: 2 out of 2 tasks 1/2: running [==================================================>] 2/2: running [==================================================>] verify: Service converged ``` > (2)查看当前swarm的service ```sh [root@docker-node3 ~]# docker service ls ID NAME MODE REPLICAS IMAGE PORTS ipiren07pmgk my_nginx replicated 2/2 nginx:latest *:8081->80/tcp ``` > (3)查看service的启动日志 ``` docker service logs my_nginx ``` > (4)查看service的详情 ``` docker service inspect my_nginx ``` > (5)查看service运行在哪些node上 ```sh [root@docker-node3 ~]# docker service ps my_nginx ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS qw6qcygfwqa5 my_nginx.1 nginx:latest docker-node3 Running Running 11 minutes ago t3v5fe3d70vt my_nginx.2 nginx:latest docker-node1 Running Running 11 minutes ago ``` > (6)水平拓展service(扩容和缩容都可以通过这个命令) ```sh docker service scale my_nginx=3 # 查看拓展结果 docker service ls docker service ps my_nginx ``` > 当某个节点的service挂掉了,swarm会自动启动一个新的service。 > (7)更新服务(https://docs.docker.com/engine/reference/commandline/service_update/) ```sh docker service update --publish-rm 8081:80 --publish-add 8082:80 my_nginx ``` > (7)删除service ```sh docker service rm my_nginx ``` #### 1.3.2 node | Command | Description | | ------------------------------------------------------------ | ------------------------------------------------------------ | | [docker node demote](https://docs.docker.com/engine/reference/commandline/node_demote/) | Demote one or more nodes from manager in the swarm | | [docker node inspect](https://docs.docker.com/engine/reference/commandline/node_inspect/) | Display detailed information on one or more nodes | | [docker node ls](https://docs.docker.com/engine/reference/commandline/node_ls/) | List nodes in the swarm | | [docker node promote](https://docs.docker.com/engine/reference/commandline/node_promote/) | Promote one or more nodes to manager in the swarm | | [docker node ps](https://docs.docker.com/engine/reference/commandline/node_ps/) | List tasks running on one or more nodes, defaults to current node | | [docker node rm](https://docs.docker.com/engine/reference/commandline/node_rm/) | Remove one or more nodes from the swarm | | [docker node update](https://docs.docker.com/engine/reference/commandline/node_update/) | Update a node | ## 2 Docker Compose ​ Dockerfile模版文件,可以很方便的定义一个单独的应用容器。然而,在日常工作中,经常碰到需要多个容器相互配合来完成某项任务的情况。例如要实现一个web项目,除了web服务器本身,往往还需要加上后端的数据库服务容器,甚至包括负载均衡容器等。 ​ Componse定位是“定义和运行多个Docker容器的应用”,它允许用户通过一个单独docker-compose.yml模版文件来定义一组相关的应用容器为一个项目(project)。 ### 2.1 Compose的概念 ​ Docker Compose 将所管理的容器分为三层,分别是项目(project)、服务(service)、容器(container) - **项目(project)**:由一组关联的应用容器组成的一个完整业务单元,在docker-compose.yml文件中定义。 - **服务(service)**:一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。 - **容器(container)**:docker容器。 ​ Docker Compose 运行目录下的所有文件(docker-compose.yml)组成一个项目,一个项目包含多个服务,每个服务中定义了容器运行的镜像、参数、依赖,一个服务可包括多个容器实例。 ​ Compose的默认管理对象是项目,通过子命令对项目中的一组容器进行便捷地生命周期管理。 Compose项目由Python编写,实现上调用了Docker服务提供的API来对容器进行管理。因此,只要所操作的平台支持Docker API,就可以在其上利用Compose来进行编排管理。 ### 2.2 Compose的安装 > (1)运行一下命令下载Docker Compose的当前稳定版本 ```sh #$(uname -s)-$(uname -m) : Linux-x86_64 curl -L "https://github.com/docker/compose/releases/download/1.26.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose # or curl -L "https://get.daocloud.io/docker/compose/releases/download/1.26.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose ``` > 要安装其它版本的Compose,请替换1.26.0 为要使用的Compose版本。 > (2)将可执行权限应用于二进制文件: ```sh chmod +x /usr/local/bin/docker-compose ``` > (3)添加到环境中 ```sh # ln -s: 建立软链接 ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose ``` > (4)测试安装 ```sh docker-compose --version ``` > 卸载Docker Compose ```sh rm /usr/local/bin/docker-compose ``` ### 2.3 docker-compose.yml ​ 模版文件是使用Compose的核心,默认的模版文件名称为docker-compose.yml,格式为yaml格式。 ```yaml version: '2.0' #版本2.0 services: #定义服务 nginx: #nginx容器 image: nginx:1.18.0 #指定镜像版本 ports: - 80:80 # 映射端口 - 433:433 volumes: #挂载数据 宿主机目录(一定要存在):容器目录 - /data/log:/var/log/nginx - /data/www:/var/www - /etc/letsencrypt:/etc/letsencrypt ``` > 常见指令 > version:版本 ``` version: '2.0' # 版本2.0 与swarm结合 要3.0以上 ``` > services:定义服务 ``` services: # 定义服务 ``` > image:指定服务的镜像名称或镜像ID。如果镜像在本地不存在,Compose将会拉取镜像。 ``` image: nginx ``` > ports:端口映射 host:container ```yaml ports: - 8000:80 ``` > build:指定dockerfile,Compose将会利用它自动构建这个镜像,然后使用这个镜像启动服务容器构建自己的应用。 ``` build: /path/dockerfilename build: ./path/dockerfilename ``` > command:使用command可以覆盖容器启动后默认执行的命令 ```sh command:echo $HOME command:["echo", "$HOME"] command: redis-server /usr/local/etc/redis/redis.conf #先创建目录和文件 ``` > depend_on:确定容器的启动顺序 ``` depends_on: - db #服务器 - redis ``` > environment:设置镜像变量 ``` environment: RACK_ENV: development SHOW: 'true' SESSION_SECRET: ``` > volumes:挂载一个目录或者一个已存在的数据卷,可以直接使用[HOST:CONTAINER]这样的格式,或者使用[HOST:CONTRAINER:ro]这样的格式,后者对于容器来说,数据卷是只读的,这样可以有效保护宿主机的文件系统。 ``` volumes: - /data/nginx/conf.d:/etc/nginx/conf.d - /data/log:/var/log/nginx - /data/www:/var/www ``` > 注意:如果目录或者文件不存在将启动不成功,所以要先创建好目录或文件。 > networks:定义网络 ```yaml # 默认网络 networks: default: driver: bridge # 自定义网络 networks: front: driver: bridge back: driver: bridge driver_opts: foo: "1" bar: "2" # 使用现有网络 # docker network create net-a --driver bridge networks: default: external: name: net-a ``` > 案例 ```yaml version: "2.0" services: nginx: image: nginx:1.18.0 restart: always container_name: nginx environment: - TZ=Asia/beijing ports: - 80:80 - 443:443 volumes: - /docker/nginx/log:/var/log/nginx - /docker/nginx/www:/etc/nginx/html - /etc/letsencrypt:/etc/letsencrypt mysql: restart: always image: mysql:5.7.30 container_name: mysql5.7 ports: - 13306:3306 command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci --explicit_defaults_for_timestamp=true --lower_case_table_names=1 --default-time-zone=+8:00 environment: MYSQL_ROOT_PASSWORD: "root" volumes: - "/docker/mysql/db:/var/lib/mysql" redis: image: redis:5.0.9 container_name: redis environment: - TZ=Asia/beijing ports: - 6379:6379 volumes: - /docker/redis/data:/data ``` > 运行: ``` docker-compose up -d ``` > 查看 ``` docker-compose ps ``` ### 2.4 Compose的常见命令 > ps:列出所有运行容器 ``` docker-compose ps ``` > logs:查看服务日志输出 ``` docker-compose logs ``` > port:打印绑定的公共端口 ``` docker-compose port nginx 80 ``` > build:构建或重新构建服务 ``` docker-compose build ``` > start:启动指定服务已存在的容器 ``` docker-compose start nginx ``` > stop:停止已运行的服务的容器 ``` docker-compose stop nginx ``` > rm:删除指定服务的容器 ``` docker-compose rm nginx ``` > up:通过docker-compose.yml,自动完成包括构建镜像,(重新)创建服务,启动服务,并关联服务相关容器的一系列操作 ``` docker-compose up -d ``` ​ 可以说,大部分时候都可以直接通过该命令来启动一个项目。默认情况,docker-compose up启动的容器都在前台,控制台将会同时打印所有容器的输出信息,可以很方便进行调试。当通过ctrl+c停止命令时,所有容器将会停止。 ​ 如果使用docker-compose up -d,将会在后台启动并运行所有的容器,一般推荐生产环境下使用该选项。 > run:在一个服务上执行一个命令 ```sh docker-compose run nginx bash ``` > down:停止并删除容器 ``` docker-compose down ``` ​ -v:可以删除容器使用的数据。 > exec:进入到某个service ``` docker-compose exec nginx sh ``` ### 2.5 与Swarm一起使用Compose > 传统手动方式实现 > 01-创建mysql容器[创建完成等待一会,注意mysql的版本] ``` docker run -d --name mysql -v v1:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=examplepass -e MYSQL_DATABASE=db_wordpress mysql:5.6 ``` > 02-创建wordpress容器[将wordpress的80端口映射到centos的8080端口] ``` docker run -d --name wordpress --link mysql \ -e WORDPRESS_DB_HOST=mysql:3306 \ -e WORDPRESS_DB_USER=root \ -e WORDPRESS_DB_PASSWORD=examplepass \ -e WORDPRESS_DB_NAME=db_wordpress \ -p 8080:80 wordpress ``` > 03-查看默认bridge的网络,可以发现两个容器都在其中 ```sh # docker network inspect bridge [root@docker-node1 bin]# docker network inspect bridge [ { "Name": "bridge", "Id": "f53756f225a342a034fb2a63d2153cf1aab7ed93ccc51e90d1599f905fd217c4", "Created": "2021-04-23T07:41:00.584829945Z", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "172.17.0.0/16", "Gateway": "172.17.0.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "781eee48426c5e6ef3ef8b694685fefc748dc058feab9d14aeff22c0f9ca9c85": { "Name": "mysql", "EndpointID": "c0a7422be801b644e4c49af6101d0d931d8affef29526924486007921d985b9d", "MacAddress": "02:42:ac:11:00:02", "IPv4Address": "172.17.0.2/16", "IPv6Address": "" }, "95c7843f399e046702558498f4266c094f91ef8b0a9dda03afb0da46cda1b7f0": { "Name": "wordpress", "EndpointID": "d1f583302736150ba53a3beac60b8e0df690fa096242b40eb788c3897e70a3b8", "MacAddress": "02:42:ac:11:00:03", "IPv4Address": "172.17.0.3/16", "IPv6Address": "" } }, "Options": { "com.docker.network.bridge.default_bridge": "true", "com.docker.network.bridge.enable_icc": "true", "com.docker.network.bridge.enable_ip_masquerade": "true", "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0", "com.docker.network.bridge.name": "docker0", "com.docker.network.driver.mtu": "1500" }, "Labels": {} } ] ``` > 使用compose方式实现 ​ docker-compose.yml可以来一次配置、启动多个容器,在 Swarm 集群中也可以使用 compose 文件 (docker-compose.yml) 来配置、启动多个服务。 ```yaml version: '3.1' services: wordpress: image: wordpress restart: always ports: - 8080:80 environment: WORDPRESS_DB_HOST: db WORDPRESS_DB_USER: exampleuser WORDPRESS_DB_PASSWORD: examplepass WORDPRESS_DB_NAME: exampledb volumes: - wordpress:/var/www/html deploy: mode: replicated replicas: 3 restart_policy: condition: on-failure delay: 5s max_attempts: 3 update_config: parallelism: 1 delay: 10s db: image: mysql:5.7 restart: always environment: MYSQL_DATABASE: exampledb MYSQL_USER: exampleuser MYSQL_PASSWORD: examplepass MYSQL_RANDOM_ROOT_PASSWORD: '1' volumes: - db:/var/lib/mysql volumes: wordpress: db: ``` > 启动项目 ```sh docker stack deploy -c stack.yml wordpress # or docker-compose -f stack.yml up ``` ```sh # 启动项目 [root@docker-node3 wordpress]# docker stack deploy -c stack.yml wordpress Ignoring unsupported options: restart Creating network wordpress_default Creating service wordpress_wordpress Creating service wordpress_db # 查看所有stack [root@docker-node3 wordpress]# docker stack ls NAME SERVICES ORCHESTRATOR wordpress 2 Swarm # 查看项目情况 [root@docker-node3 wordpress]# docker stack ps wordpress ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS zq8o2v82k70x wordpress_db.1 mysql:5.7 docker-node2 Running Running 32 seconds ago qkyfqomcbhdr wordpress_wordpress.1 wordpress:latest docker-node3 Running Running 35 seconds ago km0p1dsdsfiu wordpress_wordpress.2 wordpress:latest docker-node1 Running Running 35 seconds ago # 查看网络情况 [root@docker-node3 wordpress]# docker network inspect wordpress_default [ { "Name": "wordpress_default", "Id": "yvpkijc9v35ctolr6d5ccgo58", "Created": "2021-04-23T08:05:37.534995919Z", "Scope": "swarm", "Driver": "overlay", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "10.0.3.0/24", "Gateway": "10.0.3.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "52b2b77d3003677683955da58b3c8ecc0d480ba2e666b1ea826ab725f8c54946": { "Name": "wordpress_wordpress.1.qkyfqomcbhdruehyptb3rc47a", "EndpointID": "9eba86520d8c48e327b2e612835344d28088327c0e53aae7e18c86a406b733c2", "MacAddress": "02:42:0a:00:03:04", "IPv4Address": "10.0.3.4/24", "IPv6Address": "" }, "lb-wordpress_default": { "Name": "wordpress_default-endpoint", "EndpointID": "f066555bfdfe394deef88ba7c5f1fbe86b9dc42af0539307bfab9c811cb3f050", "MacAddress": "02:42:0a:00:03:06", "IPv4Address": "10.0.3.6/24", "IPv6Address": "" } }, "Options": { "com.docker.network.driver.overlay.vxlanid_list": "4099" }, "Labels": { "com.docker.stack.namespace": "wordpress" }, "Peers": [ { "Name": "e2f6c8a83111", "IP": "192.168.56.102" }, { "Name": "f5d4a69f597c", "IP": "192.168.56.100" }, { "Name": "ca0a7539f854", "IP": "192.168.56.101" } ] } ] ``` ```sh # 使用docker-compose -f stack.yml up命令启动 # 查看启动情况 [root@docker-node3 wordpress]# docker-compose -f stack.yml ps WARNING: Some services (wordpress) use the 'deploy' key, which will be ignored. Compose does not support 'deploy' configuration - use `docker stack deploy` to deploy to a swarm. Name Command State Ports ----------------------------------------------------------------------------------------------------- wordpress_db_1 docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp wordpress_wordpress_1 docker-entrypoint.sh apach ... Up 0.0.0.0:8080->80/tcp,:::8080->80/tcp ``` > 用docker-compose启动后查询容器列表,发现wordpress只启动了一个。提示内容提示我们使用`docker stack deploy`方式启动项目。 > docker stack相关命令 | Command | Description | | ------------------------------------------------------------ | ---------------------------------------------- | | [docker stack deploy](https://docs.docker.com/engine/reference/commandline/stack_deploy/) | Deploy a new stack or update an existing stack | | [docker stack ls](https://docs.docker.com/engine/reference/commandline/stack_ls/) | List stacks | | [docker stack ps](https://docs.docker.com/engine/reference/commandline/stack_ps/) | List the tasks in the stack | | [docker stack rm](https://docs.docker.com/engine/reference/commandline/stack_rm/) | Remove one or more stacks | | [docker stack services](https://docs.docker.com/engine/reference/commandline/stack_services/) | List the services in the stack | 访问 http://192.168.56.103:8080 or http://192.168.56.102:8080 or http://192.168.56.101:8080 > 使用docker serivce命令查看服务启动情况 ```sh [root@docker-node3 wordpress]# docker service ls ID NAME MODE REPLICAS IMAGE PORTS fwxmviygv4ag my_nginx replicated 2/2 nginx:latest *:8082->80/tcp 8wj0zxilwzg1 wordpress_db replicated 1/1 mysql:5.7 rot7u9ljm18g wordpress_wordpress replicated 2/2 wordpress:latest *:8080->80/tcp [root@docker-node3 wordpress]# docker service ps rot7u9ljm18g ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS on9493gxx8at wordpress_wordpress.1 wordpress:latest docker-node2 Running Running 3 hours ago msesazsx0vh5 wordpress_wordpress.2 wordpress:latest docker-node1 Running Running 2 hours ago [root@docker-node3 wordpress]# docker service ps 8wj0zxilwzg1 ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS usqk7qhpla23 wordpress_db.1 mysql:5.7 docker-node3 Running Running 3 hours ago ``` ## 3 配置私有仓库(Harbor)