补充-Docker
lg
# 1 Docker概述
# 1.1 容器化和虚拟化
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器或Windows 机器上,也可以实现虚拟化。(百度百科)
# 1.1.1 虚拟化
在最早的时候,我们想要在线上部署一个应用。我们需要先购买服务器,然后安装操作系统及各种依赖环境,最后进行应用的部署。
问题:
- 部署应用上线应用过程时间非常长
- 购买服务器的花费不菲
- 物理服务器的资源容易浪费
- 迁移和扩展比较困难
因为有上面的问题,后来出现的虚拟化技术。 虚拟化技术就是在操作系统上多加了一个虚拟化层(Hypervisor),可以将物理机的CPU、内存、硬盘、网络等资源进行虚拟化,再通过虚拟化出来的空间上安装操作系统,构建虚拟的应用程序执行环境。这就是我们通常说的虚拟机。
比如:VMware 、VirtualBox、KVM等。
虚拟化技术的优点:
- 提升IT效率
- 降低运维成本
- 更快地部署工作负责
- 提高应用性能
- 提高服务器可用性
- 消除服务器梳理剧增情况和复杂性
虚拟机的缺点:
占用资源较多
性能较差
扩展能力较差
环境迁移能力较差
于是容器化技术诞生了。
# 1.1.2 容器化
容器化起源集装箱。集装箱 ——全球物流系统中一个非常重要的发明。在运输之前一次性将货物封装好到集装箱里面,之后的集装箱直接放到卡车、火车、货轮上,而且集装箱是统一标准的,自然容易机械化,因此集装箱的革命,节省了大量的资源、物流成本大大降低。
借鉴了集装箱的解决方案,提出了类似集装箱的方式封装应用以及它的运行环境,将应用和环境打包成一个轻量化、可移植、自包含的容器。
容器是轻量级的,因为它们不需要虚拟机管理程序的额外负载,而是直接在主机的内核中运行。
这意味着与使用虚拟机相比,可以在给定的硬件组合上运行更多的容器。
甚至可以在实际上是虚拟机的主机中运行Docker容器
容器有四个特点:
容器共享宿主机内核
容器使用内核的功能对进程进行分组和资源限制
容器通过命名空间保证隔离
容器像是轻量级的VM(占用空间更少,速度更快),但不是虚拟机
# 1.2 Docker概述
# 1.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的实现。
# 1.2.2 Docker的应用场景
快速,一致地交付应用程序、镜像打包环境,避免了环境不一致的问题 Docker可以为开发人员提供 标准化 的本地工作环境给应用程序和服务,从而简化了开发生命周期。容器非常适合持续集成和持续交付(CI / CD)工作流程
如果有以下场景可以使用Docker交付:
开发人员在本地编写代码,并使用Docker容器与其他同事共享劳动成果。
使用Docker将应用程序推送到测试环境中,并执行自动和手动测试。
当开发人员发现错误时,他们可以在开发环境中对其进行修复,然后将其重新部署到测试环境中以进行测试和验证。
测试完成后,将修补程序推送给生产环境就像将更新的镜像推送到生产环境一样简单。
响应式部署和扩展 Docker是基于容器的平台允许高度可移植的工作负载。Docker容器可以在开发人员的本地笔记本电脑上,数据中心中的物理或虚拟机上,云提供商上或混合环境中运行。 Docker的可移植性和轻量级的特性还使可以轻松地动态管理工作负载,并根据业务需求指示实时扩展或拆除应用程序和服务。
在同一个硬件上运行更多工作负载 Docker轻巧快速。它为基于虚拟机管理程序的虚拟机提供了可行,经济高效的替代方案,因此我们可以利用更多的计算能力来实现业务目标。Docker非常适合于高密度环境以及中小型部署,而需要用更少的资源做更多的事情。
# 1.2.3 为什么要使用Docker
Docker容器虚拟化的好处
Docker提供了一种更为聪明的方式,通过容器来打包应用,解耦 应用和运行平台。意味着迁移的时候,只需要在新的服务器上启动需要 的容器就可以了,无论新旧服务器是否是同一类型的平台。
Docker在开发和运维中的优势
更快速的交付和部署
更高效的资源利用
更轻松的迁移和扩展
更简单的更新管理
提供完整的软件生态系统: 镜像管理、资源隔离、文件系统、网络隔离、流程管理及服务发现
# 1.2.4 Docker与虚拟机比较
Docker是一种轻量级的虚拟化方式。与传统虚拟机技术的特性比较如下表:
传统的虚拟机方式提供的是相对封闭的隔离。Docker利用Linux系统上的多种防护技术实现了严格的隔离可靠性,并且可以整合众多安全工具。从 1.3.0版本开始,Docker重点改善了容器的安全控制和镜像的安全机制, 极大提高了使用Docker的安全性。
# 2 Docker快速实战
# 2.1 Docker核心组件
# 2.1.1 Docker引擎
Docker使用客户端-服务器架构
Docker客户端是用户与Docker交互的主要方式,与Docker守护进程(Docker引擎)进行通信
该守护进程完成了构建,运行和分发Docker容器的繁重工作
Docker客户端和守护程序可以在同一系统上运行,也可以将Docker客户端连接到远程Docker守护程序。
Docker客户端和守护程序在UNIX套接字或网络接口上使用REST API进行通信。
Docker守护进程侦听Docker API请求并管理Docker对象,例如镜像,容器,网络和卷等。
守护程序还可以与其他守护程序通信以管理Docker服务
# 2.1.2 Docker镜像
Docker镜像类似于虚拟机镜像,可以将它理解为一个只读的模板。
镜像是基于联合(Union)文件 系统的一种层式的结构,由一系列指令一步一步构建出来。
比如:
添加一个文件;
执行一个命令;
打开一个端口。
镜像是创建Docker容器的基础。通过版本管理和增量的文件系统, Docker提供了一套十分简单的机制来创建和更新现有的镜像,用户可以从网上下载一个已经做好的应用镜像,并直接使用
我们可以利用Dockerfile构建自己的镜像。
# 2.1.3 Docker容器
Docker容器类似于一个轻量级的沙箱,Docker利用容器来运行和隔离应用。
容器是镜像的一个运行实例。
可以将其启动、开始、停止、删除,而这些容器都是彼此相互隔离的、互不可见的。 可以把容器看做是一个简易版的Linux系统环境(包括root用户权限、进程空间、用户空间和网络空间等)以及运行在其中的应用程序打包而成的盒子。
容器是基于镜像启动起来的,容 器中可以运行一个或多个进程。
镜像是Docker生命周期 中的构建或打包阶段,而容器则是启动或执行阶段。
镜像自身是只读的。容器从镜像启动的时候,会在镜像的最上层创建一个可写层。
# 2.1.4 Docker仓库
Docker仓库类似于代码仓库,它是Docker集中存放镜像文件的场所。
仓库注册服务器(Registry)是存放仓库的地方,其上往往存放着多个仓库
每个仓库集中存放某一类镜像,往往包 括多个镜像文件,通过不同的标签(tag)来进行区分
根据所存储的镜像公开分享与否,Docker仓库可以分为公开仓库 (Public)和私有仓库(Private)两种形式。
目前,最大的公开仓库是 官方提供的Docker Hub,其中存放了数量庞大的镜像供用户下载。
国内不少云服务提供商(如时速云、阿里云等)也提供了仓库的本地源,可以提供稳定的国内访问。
Docker也支持用户在本地网络内创建一个只能自己访问的私有仓库。
# 2.2 Docker安装
Docker在主流的操作系统和云平台上都可以使用,包括Linux操作 系统(如Ubuntu、Debian、CentOS、Redhat等)、MacOS操作系统和 Windows操作系统,以及AWS等云平台。
Docker官网:https://docs.docker.com/
# 2.2.1 卸载历史版本
#查看安装
yum list installed | grep docker
#卸载
yum -y remove containerd.io.x86_64
yum -y remove docker-ce.x86_64
yum -y remove docker-ce-cli.x86_64
#删库
rm -rf /var/lib/docker
# 2.2.2 安装官方yum源
yum install -y yum-utils
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# 2.2.3 安装Docker引擎
yum install -y docker-ce docker-ce-cli containerd.io
2.4 启动docker
#开机启动
systemctl enable docker
#启动
systemctl start docker
#查看Docker状态
docker info
# 2.3 Docker操作
# 2.3.1 使用Docker镜像
# 获取镜像
docker pull NAME[:TAG] (拉取最新的镜像)
Dockerhub
描述一个镜像需要包括“名称+标签”信息
例如:
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
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql 5.7.30 9cfcce23593a 6 weeks ago 448MB
可以通过man docker-images查看更多命令参数
# 添加镜像标签
docker tag [原镜像名:tag号] [目标镜像名:tag号] # 如果tag号缺省,默认latest
[root@localhost ~]# docker tag mysql:5.7.30 mysql5
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql5 latest 9cfcce23593a 6 weeks ago 448MB
mysql 5.7.30 9cfcce23593a 6 weeks ago 448MB
# 查看镜像详细信息
docker inspect NAME[:TAG]
[root@localhost ~]# docker inspect mysql:5.7.30
[
{
"Id": "sha256:9cfcce23593a93135ca6dbf3ed544d1db9324d4c40b5c0d56958165bfaa2d46a",
"RepoTags": [
"mysql:5.7.30"
],
"RepoDigests": [
"mysql@sha256:32f9d9a069f7a735e28fd44ea944d53c61f990ba71460c5c183e610854ca4854"
],
"Parent": "",
"Comment": "",
"Created": "2020-06-09T07:21:19.100938536Z",
"Container": "0b42e56165f339e2bf9e1799e0125db6fbb1f54a24a79c1b4d621248bc24927a",
"ContainerConfig": {
"Hostname": "0b42e56165f3",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"3306/tcp": {},
"33060/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"GOSU_VERSION=1.12",
"MYSQL_MAJOR=5.7",
"MYSQL_VERSION=5.7.30-1debian10"
],
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"CMD [\"mysqld\"]"
],
"ArgsEscaped": true,
"Image": "sha256:b978dc18619b1ca9b160e4f1592465b0450973794b7a0e4bd149d4453f5256be",
"Volumes": {
"/var/lib/mysql": {}
},
"WorkingDir": "",
"Entrypoint": [
"docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": {}
},
"DockerVersion": "18.09.7",
"Author": "",
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"3306/tcp": {},
"33060/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"GOSU_VERSION=1.12",
"MYSQL_MAJOR=5.7",
"MYSQL_VERSION=5.7.30-1debian10"
],
"Cmd": [
"mysqld"
],
"ArgsEscaped": true,
"Image": "sha256:b978dc18619b1ca9b160e4f1592465b0450973794b7a0e4bd149d4453f5256be",
"Volumes": {
"/var/lib/mysql": {}
},
"WorkingDir": "",
"Entrypoint": [
"docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": null
},
"Architecture": "amd64",
"Os": "linux",
"Size": 448082853,
"VirtualSize": 448082853,
"GraphDriver": {
"Data": {
"DeviceId": "12",
"DeviceName": "docker-8:3-1784452- 7877d481a821545822e9ced737187d7f95804b5cf1594725538ccdfe66d3dbfc",
"DeviceSize": "10737418240"
},
"Name": "devicemapper"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74",
"sha256:365386a39e0ea80fcf2a4e3a3cd0e490f073d976133b96dd7f5e2cd1579a8ff5",
"sha256:c3f46b20a0d3c6532ec63cb2f5475a0a33c8e4c2f22a0a2184d7d79d2f970b37",
"sha256:66c45123fd43c21cc8be641b73bf2747adf326c6e07d08eadf9b6c829ad575b3",
"sha256:61cbb8ea64815ee524388e222d32855953ff71bce2a2049185232b3c0463fa93",
"sha256:44853bb6727490ada4379f3348acbf52b3e7abb63427ce42ca118e11a7b94018",
"sha256:3a2464d8e0c0697f6fb252a602a6ab95542e5ad10aacc9277d269a182db8dc30",
"sha256:91ae264962fbfc55b25a1b59378024ef08833c7003823136e73f43985ecda5ee",
"sha256:8f0182ef7c8cff5ae6b305dff6d7555a249a1e24bfbd94e4f25e75090e763ae3",
"sha256:ac76579057880b4e115cc46f952aa9b1c92f1f2adbca8ebba810951200e9c288",
"sha256:c90a34afcab00e4d70d1672b27c4780f6eb881b6cd51c3da492497b15be0b24d"
]
},
"Metadata": {
"LastTagTime": "0001-01-01T00:00:00Z"
}
}
]
返回的是一个JSON格式的消息,如果我们只要其中一项内容时, 可以使用参数-f来指定
[root@localhost ~]# docker inspect mysql:5.7.30 -f {{".Architecture"}}
amd64
# 搜寻镜像
docker search 名称
[root@localhost ~]# docker search mysql
NAME DESCRIPTION
STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source
relation… 9760 [OK]
mariadb MariaDB is a community-developed fork of
MyS… 3564 [OK]
mysql/mysql-server Optimized MySQL Server Docker images.
Create… 717 [OK]
centos/mysql-57-centos7 MySQL 5.7 SQL database server
78
mysql/mysql-cluster Experimental MySQL Cluster Docker images.
Cr… 73
dockerhub仓库镜像, 私有仓库无法搜索到
# 删除镜像
docker rmi NAME[:TAG]
[root@localhost ~]# docker rmi mysql:5.7.30
Untagged: mysql:5.7.30
Untagged:
mysql@sha256:32f9d9a069f7a735e28fd44ea944d53c61f990ba71460c5c183e610854ca4854
# 当同一个镜像拥有多个标签的时候,docker rmi命令只
# 是删除该镜像多个标签中的指定标签而已,并不影响镜像文件。
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql5 latest 9cfcce23593a 6 weeks ago 448MB
#当镜像只剩下一个标签的时候,使用docker rmi命令会彻底删除镜像
[root@localhost ~]# docker rmi mysql5:latest
Untagged: mysql5:latest
Deleted: sha256:9cfcce23593a93135ca6dbf3ed544d1db9324d4c40b5c0d56958165bfaa2d46a
Deleted: sha256:98de3e212919056def8c639045293658f6e6022794807d4b0126945ddc8324be
Deleted: sha256:17e8b88858e400f8c5e10e7cb3fbab9477f6d8aacba03b8167d34a91dbe4d8c1
Deleted: sha256:c04c087c2af9abd64ba32fe89d65e6d83da514758923de5da154541cc01a3a1e
Deleted: sha256:ab8bf065b402b99aec4f12c648535ef1b8dc954b4e1773bdffa10ae2027d3e00
Deleted: sha256:09687cd9cdf4c704fde969fdba370c2d848bc614689712bef1a31d0d581f2007
Deleted: sha256:b704a4a65bf536f82e5d8b86e633d19185e26313de8380162e778feb2852011a
Deleted: sha256:c37206160543786228aa0cce738e85343173851faa44bb4dc07dc9b7dc4ff1c1
Deleted: sha256:12912c9ec523f648130e663d9d4f0a47c1841a0064d4152bcf7b2a97f96326eb
Deleted: sha256:57d29ad88aa49f0f439592755722e70710501b366e2be6125c95accc43464844
Deleted: sha256:b17c024283d0302615c6f0c825137da9db607d49a83d2215a79733afbbaeb7c3
Deleted: sha256:13cb14c2acd34e45446a50af25cb05095a17624678dbafbcc9e26086547c1d74
使用镜像ID删除镜像
docker rmi IMAGE ID
REPOSITORY TAG IMAGE ID CREATED SIZE
tomcat 8.5.56-jdk8-openjdk e010d327a904 6 weeks ago 529MB
mysql5 latest 9cfcce23593a 6 weeks ago 448MB
mysql 5.7.30 9cfcce23593a 6 weeks ago 448MB
[root@localhost ~]# docker rmi 9cfcce23593a
Error response from daemon: conflict: unable to delete 9cfcce23593a (must be forced) - image is referenced in multiple repositories
如果有容器正在运行该镜像,则不能删除
如果想强行删除用 -f (不推荐)
# 上传镜像
docker push NAME[:TAG]
上传镜像到仓库,默认上传到Docker Hub官方仓库(需要登录)
# 2.3.2 操作Docker容器
# 创建容器
docker create NAME[:TAG]
可以加选项参数
-i 交互模式
-t 伪终端
-d 后台运行
-rm 容器退出后是否自动删除
docker create -it nginx
# 启动容器
docker start 容器id
docker start 9cfcce23593a
查看容器状态
docker ps 查看运行的容器
#查看运行的容器
docker ps
#查看所有容器
docker ps -a
# 新建并启动容器
docker run NAME[:TAG]
相当于docker create+docker start
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 时间 (默认10秒)
首先向容器发送SIGTERM信号,等待一段超时时间(默认为10 秒)后,再发送SIGKILL信号来终止容器
docker kill 容器id
docker kill命令会直接发送SIGKILL信号来强行终止容器
docker stop ce554267d7a4 -t 5
docker kill ce554267d7a4
处于终止状态的容器,可以通过docker start命令来重新启动
docker start ce554267d7a4
docker restart命令会将一个运行态的容器先终止,然后再重新启动
docker restart ce5
# 进入容器
docker exec -it [容器ID] /bin/bash
早期有attach命令,对于阻塞命令会等待,所以不方便
在Docker 1.3.0后提供了exec 可以在容器内直接执行任意命令
docker exec -it 243c32535da7 /bin/bash
无论在容器内进行何种操作,依据依据镜像创建的其他容器都不会受影响(由于namespace的隔离)(将数据持久化的除外) exec: 容器执行某操作,操作为容器ID后边的命令 -it: 以伪终端模式,这样我们就相当于进入到容器中了
# 查看容器
docker inspect [容器ID]
docker inspect 243c32535da7
docker stats [容器ID]
docker stats 243c32535da7
# 删除容器
docker rm [容器ID]
docker rm 243c32535da7
docker rm命令只能删除处于终止或退出状态的容器,并不能删除还处于运行状态的容器
要直接删除一个运行中的容器,可以添加-f参数。Docker会先 发送SIGKILL信号给容器,终止其中的应用,之后强行删除
# 2.3.3 访问Docker仓库
Docker Hub是最大的公共镜像仓库(https://hub.docker.com/)
在公共仓库中注册一个账号,每ID可以免费拥有1个私有镜像
# 登录仓库
docker login -u username -p password
默认登陆的是docker hub
[root@localhost ~]# docker login -u gavinli80s -p ljp123465
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
登录成功的用户可以上传个人制造的镜像。
用户无需登录即可通过docker search命令来查找官方仓库中的镜 像,并利用docker pull命令来将它下载到本地
# 登出仓库
docker logout
可以同时登陆多个docker仓库,因此此命令一般不执行
[root@localhost ~]# 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": "Z2F2aW5saTgwczpsanAxMjM0NjU="
}
},
"HttpHeaders": {
"User-Agent": "Docker-Client/19.03.12 (linux)"
}
}
# 2.3.4 常用软件的容器化部署
# Mysql
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
[root@localhost ~]# mysql -h192.168.72.129 -uroot -proot
Warning: Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.30 MySQL Community Server (GPL)
Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
# Tomcat
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
[root@localhost ~]# curl http://192.168.72.129:8080/
<!doctype html><html lang="en"><head><title>HTTP Status 404 – Not
Found</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;}
h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2
{font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;}
.line {height:1px;background-color:#525D76;border:none;}</style></head><body>
<h1>HTTP Status 404 – Not Found</h1><hr class="line" /><p><b>Type</b> Status
Report</p><p><b>Description</b> The origin server did not find a current
representation for the target resource or is not willing to disclose that one
exists.</p><hr class="line" /><h3>Apache Tomcat/8.5.56</h3></body></html>
[root@localhost ~]#
创建一个webapp
进入到运行中的容器tomcat:8.5.56-jdk8-openjdk
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0813f630621b tomcat:8.5.56-jdk8-openjdk "catalina.sh run" 14minutes ago Up 14 minutes stupefied_stonebraker
d93be60e1ed6 mysql:5.7.30 "docker-entrypoint.s…" 20minutes ago Up 20 minutes gracious_tharp
[root@localhost ~]# docker exec -it 08 /bin/bash
root@localhost:/usr/local/tomcat# ls
BUILDING.txt LICENSE README.md RUNNING.txt conf logs temp webapps.dist
CONTRIBUTING.md NOTICE RELEASE-NOTES bin lib native-jni-lib webapps work
root@localhost:/usr/local/tomcat# cd webapps
root@localhost:/usr/local/tomcat/webapps# ls
root@localhost:/usr/local/tomcat/webapps# mkdir app1
root@localhost:/usr/local/tomcat/webapps# cd app1
root@localhost:/usr/local/tomcat/webapps/app1# echo Hello Docker > index.html
# Nginx
docker pull nginx
docker run --name nginx1 --network host -d nginx
--name:运行的容器名称
访问nginx
# Redis
docker pull redis:5.0.9
docker run --network host -d redis:5.0.9
访问Redis
[root@localhost ~]# cd /var/redis-cluster/7001/bin/
[root@localhost bin]# ./redis-cli -h 192.168.72.129
192.168.72.129:6379>
#本机访问
[root@localhost ~]# docker exec -it 73 redis-cli
127.0.0.1:6379>
# 2.3.5 Docker命令图谱
# 3 Docker核心原理
作为一种容器虚拟化技术,Docker深度应用了操作系统的多项底层支持技术,包括:Linux操作系统的命名空间(Namespace)、控制组(Control Group)、 联合文件系统(Union File System)和Linux网络虚拟化支持。
# 3.1 内部结构
Docker Engine是具有以下主要组件的客户端-服务器应用程序:
服务器是一种长期运行的程序,称为守护程序进程( dockerd命令)。
REST API,它指定程序可以用来与守护程序进行通信并指示其操作的接口。
命令行界面(CLI)客户端(docker命令)
这么做的好处就是可以通过RestApi将客户端和服务段解耦,这样服务端就可以做到Volume/Network等做成插件化组装
# 3.2 容器生命周期
# 状态与命令对应表
状态 | 命令 |
---|---|
created: 初创建 | docker create |
running:运行状态 | docker run、docker start、docker restart、docker unpause |
stopped:停止状态 | docker stop、docker kill |
paused:暂定状态 | docker pause |
deleted:删除状态 | docker rm |
# 状态转化图
需要根据实际情况选择的操作
killed by out-of-memory(因内存不足被终止)
宿主机内存被耗尽,也被称为OOM:非计划终止
这时需要杀死最吃内存的容器
然后进行选择操作
container process exitde(异常终止)
出现容器被终止后,将进入Should restart?选择操作:
yes 需要重启,容器执行start命令,转为运行状态。
no 不需要重启,容器转为停止状态。
# 3.3 容器命名空间(NameSpace)
命名空间(namespace)是Linux内核的一个强大特性,为容器虚拟化的实现带来极大便利。
命名空间要对内存、CPU、网络IO、硬盘IO、存储空间、文件系统、网络、PID、UID、IPC等的相互隔离。
# 3.3.1 进程命名空间(PID namespace)
PID namespace 对集成PID重新编号,即两个不同的namespace下的进程可以用相同的PID。
内核为所有的PID namespace维护了一个树状结构,最顶层的是系统初始时创建的,被称作root namespace(进程号1)。
它创建的PID namespace被称作为child namespace(树的字节点),
不同的PID namespace会形成一个层级体系,所属的父节点可以看到子节点中的进程,并通过信号等方式对子节点中的进程产生影响。反过来,子节点却看不到父节点PID namespace的任何内容。
[root@localhost ~]# docker run --rm -d --network host nginx:latest
0f975c076b5d8b2f2072d184c521b3e81b92df41e9db7b8e5831f99531c4dda6
[root@localhost ~]# docker run --rm --network host -it tomcat:8.5.56-jdk8-openjdk
[root@localhost ~]# ps -ef |grep docker
root 2335 1 0 21:01 ? 00:00:00 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root 2881 2334 0 21:05 ? 00:00:00 containerd-shim -namespace moby -workdir
/var/lib/containerd/io.containerd.runtime.v1.linux/moby/30df4df442a94d7d8c244fe4
bc1f2420213cc910155a0392a0771dc6c8b62bb4 -address
/run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-
root /var/run/docker/runtime-runc
root 2958 2234 0 21:07 pts/0 00:00:00 docker run -it --network host --rm tomcat:8.5.56-jdk8-openjdk
root 3002 2334 0 21:07 ? 00:00:00 containerd-shim -namespace moby -workdir
/var/lib/containerd/io.containerd.runtime.v1.linux/moby/98292aa170640508f26e1a35
0e59fb80778487a38d79c099ba7396faf5e91231 -address
/run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-
root /var/run/docker/runtime-runc
root 3099 3064 0 21:08 pts/1 00:00:00 grep --color=auto docker
[root@localhost ~]# ps -ef |grep nginx
root 2896 2881 0 21:05 ? 00:00:00 nginx: master process nginx -g daemon off;
101 2933 2896 0 21:05 ? 00:00:00 nginx: worker process
root 3109 3064 0 21:08 pts/1 00:00:00 grep --color=auto nginx
[root@localhost ~]# ps -ef |grep tomcat
root 2958 2234 0 21:07 pts/0 00:00:00 docker run -it --network host --rm tomcat:8.5.56-jdk8-openjdk
root 3018 3002 2 21:07 pts/0 00:00:01 /usr/local/openjdk-8/bin/java
-Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -classpath
/usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomcat -Dcatalina.home=/usr/local/tomcat -Djava.io.tmpdir=/usr/local/tomcat/temp org.apache.catalina.startup.Bootstrap
start
root 3111 3064 0 21:08 pts/1 00:00:00 grep --color=auto tomcat
# 3.3.2 网络命名空间(network namespace)
通过网络命名空间,可以实现网络隔离。
网络命名空间为进程提供了一个完全独立的网络协议栈的视图,包括网络设备接口、IPv4和IPv6 协议栈、IP路由表、防火墙规则、sockets等,这样每个容器的网络就能 隔离开来。
Docker采用虚拟网络设备(Virtual Network Device)的方式,将不同命名空间的网络设备连接到一起。
默认情况下,容器中的虚 拟网卡将同本地主机上的docker0网桥连接在一起
# 3.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.3.4 挂载命名空间(Mount namespace)
类似于chroot,将一个进程放到一个特定的目录执行。
挂载命名空间允许不同命名空间的进程看到的文件结构不同,这样每个命名空间中的进程所看到的文件目录彼此被隔离。
Mount namespace通过隔离文件系统挂载点对隔离文件系统通过提供支持,是Linux支持的第一个namespace。隔离后不同mount namespace中的文件结构发生变化也不互相影响,可以通过 /proc/[pid]/mounts 查看到所有挂载在当前namespace中的文件系统,还可以通过 /proc/[pid]/mountstats 查到mount namespace中文件设备的统计信息,包括挂载文件的名字、文件系统、挂载位置等
# 3.3.5 UTS命名空间(UTS namespace)
UTS(UNIX Time-sharing System)命名空间允许每个容器拥有独立的主机名和域名,从而可以虚拟出一个有独立主机名和网络空间的环境,就跟网络上一台独立的主机一样
这样每个Docker容器就可以拥有独立的主机名和容器了,在网络上可以被视作一个独立的节点,而非宿主机上的一个进程
Docker中,每个镜像基本上都以自身提供的服务名来命名镜像的hostname,而且不会对宿主机产生任何影响,其原理就是利用了UTS namespace
默认情况下,Docker容器的主机名就是返回的容器ID
# 3.3.6 用户命名空间(user namespace)
每个容器可以有不同的用户和组id,也就是说可以在容器内使用特定的内部用户执行程序,而非本地系统上存在的用户。 每个容器内部都可以有root帐号,但跟宿主主机不在一个命名空间。 通过使用隔离的用户命名空间可以提高安全性,避免容器内进程获取到额外的权限。
# 3.4资源配额限制(CGroups)
控制组(CGroups)是Linux内核的一个特性,主要用来对共享资源进行隔离、限制、审计等。只有能控制分配到容器的资源,才能避免多个容器同时运行时对宿主机系统的资源竞争
控制组可以提供对容器的内存、CPU、磁盘IO等资源进行限制和管理
默认情况下,容器没有资源限制,并且可以使用主机内核调度程序允许的尽可能多的给定资源, 这可以在我们查看容器状态时看到 docker stats 容器ID 。Docker提供了一些方法来控制容器可以使用多少内存或CPU,在设置 docker run 命令的运行时配置标志。
# 3.4.1 CPU
docker run --cpus 1.5 --rm -d --network host nginx:latest
# 3.4.2 内存
大部分的选项取正整数,跟着一个后缀 b , k , m , g ,,表示字节,千字节,兆字节或千兆字节。
[root@localhost ~]# docker run --rm -d --network host -m 100m nginx:latest
6491a0d526a44241c744af0bf9a43d5ba80705d053cc446f051857193db40632
# 3.4.3 磁盘读写
Block I0指的是磁盘的读写,docker 可通过设置权重、限制bps和iops 的方式控制容器读写磁盘的带宽
docker run -it --device-write-bps /dev/sda:30MB ubuntu
总结如下表:
# 3.5 联合文件系统(UnionFS)
联合文件系统(UnionFS)是一种轻量级的高性能分层文件系统,它支持将文件系统中的修改信息作为一次提交,并层层叠加,同时可以将不同目录挂载到同一个虚拟文件系统下,应用看到的是挂载的最终结果
UnionFS可以把文件系统上多个目录(也叫分支)内容联合挂载到同一个目录下,而目录的物理位置是分开的
联合文件系统是实现Docker镜像的技术基础。Docker镜像可以通过分层来进行继承
# 3.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利用镜像启动一个容器时,将在镜像文件系统的最顶端再挂载一个新的可读写的层给容器。容器中的内容更新将会发生在可读写层
通过docker history查看一个镜像由哪些层组成。
docker history nginx:latest
# 3.5.2 多种文件系统比较
Docker目前支持的联合文件系统种类包括AUFS、OverlayFS、 btrfs、vfs、zfs和Device Mapper等,
如下表
通过docker info 的Storage Driver可以看到
# 3.6 容器网络
Docker的本地网络实现其实就是利用了Linux上的网络命名空间和虚拟网络设备(veth pair)
要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)与外界相通,并可以收发数据包
如果不同子网之间要进行通信,需要额外的路由机制
Docker中的网络接口默认都是虚拟的接口
Docker容器网络在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通(这样的一对接口叫做veth pair)
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
使用默认网络
docker run --rm -d --name my_nginx nginx
使用宿主机网络
docker run --rm -d --network host --name my_nginx nginx
使用物理网卡模式
[root@localhost ~]# docker network create -d macvlan --subnet=172.16.206.0/24 --gateway=172.16.206.1 -o parent=eth0 my-macvlan-net
e34965e7359cd9ac81f534a44b626b64848fc19a165fbcb9e58c0d9d0f31e1dd
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
0ecdc79fec3d bridge bridge local
5e417b84c226 host host local
e34965e7359c my-macvlan-net macvlan local
0274a191bbcf none null local
[root@localhost ~]# docker run -it --rm -d --network my-macvlan-net nginx
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
eadc1ccec8da nginx "/docker-entrypoint.…" 7 seconds ago Up 6 seconds peaceful_mclaren
[root@localhost ~]# docker inspect eadc1ccec8da
[
{
.......
"NetworkSettings": {
"Bridge": "",
"SandboxID": "fe5becb769eefe6f78c458b3781dccd5b384fda4bba3bf251fabc7488c330020",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "/var/run/docker/netns/fe5becb769ee",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "",
"Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"MacAddress": "",
"Networks": {
"my-macvlan-net": {
"IPAMConfig": null,
"Links": null,
"Aliases": [
"eadc1ccec8da"
],
"NetworkID": "e34965e7359cd9ac81f534a44b626b64848fc19a165fbcb9e58c0d9d0f31e1dd",
"EndpointID": "505faeca525c90baed4a1b5c3427bfcde50e3dfa9e5f0e1134eeed26a1d6d026",
"Gateway": "192.168.72.1",
"IPAddress": "192.168.72.2",
"IPPrefixLen": 24,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:c0:a8:48:02",
"DriverOpts": null
}
}
}
}
]
#删除网络
docker network rm e3
这样我们的启动的容器就有了单独的Mac地址,并且可以在服务器端找的到了
# 3.7 存储卷
默认情况下,在容器内创建的所有文件都存储在可写容器层上。这样做问题有:
当该容器不再存在时,数据将不会持久保存,如果另一个进程需要它,则可能很难从容器中取出数据
容器的可写层与运行容器的主机紧密耦合。不能轻易地将数据移动到其他地方写入容器的可写层需要存储驱动程序来管理文件系统。性能比直接写入主机要低
Docker为容器提供了两个选项来将文件存储在主机中,以便即使容器停止后文件也可以持久存储:
数据卷(Data Volumes):容器内数据直接映射到本地主机环境
数据卷容器(Data Volume Containers):使用特定容器维护数据卷,也成为bind mounts(绑定挂载)
如果在Linux上运行Docker,则还可以使用 tmpfs 挂载。如果在Windows上运行Docker,则还可以使用命名管道
# 3.7.1 数据卷(Data Volumes)
概念
由Docker创建和管理
是一个可供容器使用的特殊目录,它将主机操作系统目录直接映射进容器,类似于Linux中的mount操作
特性
可以在容器之间共享和重用
对数据卷内数据的修改会立马生效
对数据卷的更新不会影响镜像
卷会一直存在,直到没有容器使用,可以安全地卸载它
创建方式
显式创建卷:
docker volume create
在容器内创建卷:
docker run -v
使用场景
在多个运行中的容器之间共享数据
当要将容器的数据存储在远程主机或云提供商上时,而不是在本地
当需要将数据从一个Docker主机备份,还原或迁移到另一个Docker主机时
创建和管理卷
创建一个卷:
$ docker volume create my-vol
清单数量:
$ docker volume ls
local my-vol
检查体积:
$ docker volume inspect my-vol
[
{
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
"Name": "my-vol",
"Options": {},
"Scope": "local"
}
]
删除卷:
$ docker volume rm my-vol
启动一个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
"Mounts": [
{
"Type": "volume",
"Name": "myvol2",
"Source": "/var/lib/docker/volumes/myvol2/_data",
"Destination": "/app",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
这表明安装是一个卷,它显示了正确的源和目标,并且该安装是可读写的。 停止容器并卸载卷。卷删除是一个单独的步骤。
$ docker container stop devtest
$ docker container rm devtest
$ docker volume rm myvol2
# 3.7.2 数据卷容器(Data Volume Containers)
概念
数据卷容器也成为绑定挂载,使用绑定安装时,主机上的文件或目录将安装到容器中。该文件或目录不需要在Docker主机上已经存在。如果尚不存在,则按需创建。
可以通过容器中运行的进程来更改主机文件系统 ,包括创建,修改或删除重要的系统文件或目录
使用场景
多个容器之间共享一些持续更新的数据
Docker主机上的开发环境和容器之间共享源代码或构建工件
创建和管理数据卷容器
- 创建容器
docker run -d \
-it \
--name devtest \
-v "$(pwd)"/target:/app \
nginx:latest
- 使用docker inspect devtest验证绑定安装正确创建。查找Mounts部分:
docker inspect devtest
"Mounts": [
{
"Type": "bind",
"Source": "/root/target",
"Destination": "/app",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
$ docker container stop devtest
$ docker container rm devtest
使用只读绑定安装
对于某些开发应用程序,容器需要写入绑定安装,因此更改将传播回Docker主机。在其他时间,容器仅需要读取访问权限。
此示例修改了上面的示例,但ro通过在容器中的安装点之后添加到(默认为空)选项列表中,将目录作为只读绑定安装进行安装。如果有多个选项,请用逗号分隔。
- 启动容器
$ docker run -d \
-it \
--name devtest \
-v "$(pwd)"/target:/app:ro \
nginx:latest
- 使用docker inspect devtest验证绑定安装正确创建。查找Mounts部分:
docker inspect devtest
"Mounts": [
{
"Type": "bind",
"Source": "/root/target",
"Destination": "/app",
"Mode": "ro",
"RW": false,
"Propagation": "rprivate"
}
],
# 3.7.3 tmpfs挂载
概念
tmpfs挂载仅存储在主机系统的内存中,不会写入主机系统的文件系统中
不会持久化在磁盘上,无论是在Docker主机上还是在容器内
使用场景
可以使用它来存储非持久状态或敏感信息
在容器中使用tmpfs挂载
- 要在容器中使用 tmpfs ,请使用 --tmpfs 标志,或将 --mount 标志与 type=tmpfs 和 destination选项一起使用。没有 source 了 tmpfs 标志。以下示例在Nginx容器中的上创建一个 tmpfs 安装 /app
$ docker run -d \
-it \
--name tmptest \
--tmpfs /app \
nginx:latest
- tmpfs通过运行docker container inspect tmptest并查找以下Mounts部分来验证安装是否为安装:
docker container inspect tmptest
"Tmpfs": {
"/app": ""
},
- 卸下容器:
$ docker container stop tmptest
$ docker container rm tmptest
指定tmpfs选项
tmpfs安装允许两个配置选项,都不是必需的。如果需要指定这些选项,则必须使用--mount标志,因为该--tmpfs标志不支持它们。 tmpfs-size : tmpfs安装的大小(以字节为单位)。默认情况下不受限制。 tmpfs-mode: tmpfs的文件模式(八进制)。例如1770 (不是全局可读)
以下示例将设置tmpfs-mode为1770。
docker run -d \
-it \
--name tmptest \
--mount type=tmpfs,destination=/app,tmpfs-mode=1770 \
nginx:latest
# 4 Docker高级实战
# 4.1 DockerFile
DockerFile是一个文本格式的配置文件,用户可以使用DockerFile来快速创建自定义的镜像。
# 4.1.1 DockerFile基本结构
Dockerfile由一行行命令语句组成,并且支持以#开头的注释行
Dockerfile分为四部分:基础镜像信息、维护者信息、 镜像操作指令和容器启动时执行指令
比如:
# This Dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
# Base image to use, this must be set as the first line
FROM ubuntu:18.04
MAINTAINER docker_user docker_user@email.com
COPY . /app
RUN make /app
CMD python /app/app.py
每条指令创建一层:
FROM : 指明所基于的镜像名称,从ubuntu:18.04Docker镜像创建一个图层。
MAINTAINER:维护者信息,docker_user维护(可以不写)
COPY : 从Docker客户端的当前目录添加文件,镜像操作指令
RUN : 构建镜像时执行make命令,每运行一条RUN指令,镜像就添加新的一层,并提交(添加可写层)
CMD : 指定在容器中运行什么命令,用来指定运行容器时的操作命令
Docker镜像由只读层组成,每个只读层代表一个Dockerfile指令。这些层是堆叠的,每个层都是上一层的变化的增量
Docker可以通过读取Dockerfile指令来自动构建镜像
完整的小例子:
#在一个空目录下,新建一个名为 Dockerfile 文件
mkdir /usr/dockerfile -p
vim dockerfile-demo
#编辑 dockerfile
FROM nginx:latest
# 维护者 可以省略
MAINTAINER gavinli gavinli@docker.com
#启动容器
RUN mkdir /usr/share/nginx/html/ -p
RUN echo Hello DockerFile! > /usr/share/nginx/html/demo.html
#构建镜像 . : 根据当前上下文环境构建
docker build -f dockerfile-demo -t lagou/nginx:v1 .
#运行
docker run --rm -d -it --network host lagou/nginx:v1
浏览器访问
# 4.1.2 DockerFile指令详解
Dockerfile命令官方文档 (opens new window)
常见命令详解:
- FROM
指定所创建镜像的基础镜像,如果本地不存在,则默认会去Docker Hub下载指定镜像。
命令格式如下:
FROM [--platform=<platform>] <image> [AS <name>]
Or
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
Or
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
平时不用这么复杂的,只需如下即可
FROM <image>[:<tag>]
FROM centos:7.6.1810
任何Dockerfile中的第一条指令必须为FROM指令。并且,如果在同 一个Dockerfile中创建多个镜像,可以使用多个FROM指令(每个镜像一 次)。
- MAINTAINER
指定维护者信息,格式为MAINTAINER。可以不写
MAINTAINER gavinli gavinli@docker.com
该信息会写入生成镜像的Author属性域中
- RUN
运行指定命令
格式为
1、RUN
默认将在shell终端中运行命令,即/bin/sh-c
2、RUN["executable","param1","param2"]。
指令会被解析 为Json数组,因此必须用双引号。
exec执行,不会启动shell环境
指定使用其他终端类型可以通过此方式实现,例如
RUN["/bin/bash","-c","echo hello"]
每条RUN指令将在当前镜像的基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用\
来换行
在shell形式中,可以使用\(反斜杠)将一条RUN指令继续到下一行。例如,考虑以下两行:
RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'
它们在一起等效于以下这一行:
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
要使用'/bin/sh'以外的其他 shell ,请使用 exec 形式传入所需的 shell 。例如:
RUN ["/bin/bash", "-c", "echo hello"]
注意 在JSON格式中,必须转义反斜杠。在Windows中,反斜杠是路径分隔符,这一点尤其重要。由于无效的JSON,以下行否则将被视为shell形式,并以意外的方式失败:
RUN ["c:\windows\system32\tasklist.exe"]
此示例的正确语法为:
RUN ["c:\\windows\\system32\\tasklist.exe"]
- CMD
CMD指令用来指定启动容器时默认执行的命令。它支持三种格式:
CMD["executable","param1","param2"]使用exec执行,是推荐使用的方式;
CMD [ "sh", "-c", "echo $HOME" ]
CMD command param1 param2在/bin/sh中执行,提供给需要交互的应用;
CMD echo $HOME
CMD["param1","param2"]提供给ENTRYPOINT的默认参数
CMD [ "echo", "$HOME" ]
每个Dockerfile只能有一条CMD命令。如果指定了多条命令,只有最后一条会被执行
如果用户启动容器时手动指定了运行的命令(作为run的参数), 则会覆盖掉CMD指定的命令
docker run --rm centos ping 192.168.1.1
- COPY
复制本地主机的下的内容到镜像中的路径下。目标路径不存在时,会自动创建
格式如下:
COPY
src:为Dockerfile所在目录的相对路径、文件或目录
dest:镜像中的目标路径,相对和绝对都可以支持通配符和正则
COPY hom* /mydir/
COPY hom?.txt mydir/
当使用本地目录为源目录时,推荐使用COPY
- ENV
指定环境变量,在镜像生成过程中会被后续RUN指令使用,在镜像启动的容器中也会存在。
格式为ENV或ENV=...
ENV 指令有两种形式
第一种形式
ENV <key> <value>
会将一个变量设置为一个值。第一个空格之后的整个字符串将被视为<value>
-包括空格字符。该值将为其他环境变量解释,因此如果不对引号字符进行转义,则将其删除。第二种形式
ENV <key>=<value> ...
允许一次设置多个变量。请注意,第二种形式在语法中使用等号 = ,而第一种形式则不使用等号 = 。与命令行解析一样,引号和反斜杠可用于在值中包含空格。
例如:
ENV myName="John Doe" myDog=Rex\ The\ Dog \
myCat=fluffy
和
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy
是一样的
ENV 从结果镜像运行容器时,使用设置的环境变量将保留。可以使用查看值 docker inspect ,
并使用更改它们 docker run --env <key>=<value>
。
docker run --env<key>=<value> built_image
- ADD
该命令将复制指定的路径下的内容到容器中的路径下
格式为ADD
ADD hom* /mydir/
ADD hom?.txt /mydir/
ADD test.txt /absoluteDir/
ADD test.txt relativeDir/
其中可以是Dockerfile所在目录的一个相对路径(文件或目录),也可以是一个URL,还可以是一个tar文件(如果为tar文件,会自 动解压到路径下)。可以是镜像内的绝对路径,或者相对于工作目录(WORKDIR)的相对路径
与COPY的区别
1、Dockerfile中的COPY指令和ADD指令都可以将主机上的资源复制或加入到容器镜像中,都是在构建镜像的过程中完成的。
2、COPY指令和ADD指令的区别在于是否支持从远程URL获取资源。COPY指令只能从执行docker build所在的主机上读取资源并复制到镜像中。而ADD指令还支持通过URL从远程服务器读取资源并复制到镜像中。
3、满足同等功能的情况下,推荐使用COPY指令。ADD指令更擅长读取本地tar文件并解压缩
4、当要读取URL远程资源的时候,并不推荐使用ADD指令,而是建议使用RUN指令,在RUN指令中执行wget 或curl命令
- ENTRYPOINT
指定镜像的默认入口命令,该入口命令会在启动容器时作为根命令执行,所有传入值作为该命令的参数
ENTRYPOINT有两种形式:
在EXEC的形式,这是优选的形式 ENTRYPOINT ["executable", "param1", "param2"] ,这种执行的是 executable param1 param2
带壳形式: ENTRYPOINT command param1 param2 。这中其实执行的是 /bin/sh -c command param1 param2
多个 ENTRYPOINT 只有最后一条生效
ENTRYPOINT ["echo", "4"]
ENTRYPOINT ["echo", "5"]
ENTRYPOINT ["echo", "6"]
- VOLUME
创建一个数据卷挂载点
格式为VOLUME["/data"]
FROM centos:7.6.1810
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol
RUN echo "hello" > /myvol/greeting
可以从本地主机或其他容器挂载数据卷,一般用来存放数据库和需要保存的数据等
- WORKDIR
为后续的RUN、CMD和ENTRYPOINT指令配置工作目录
格式为WORKDIR/path/to/workdir
可以使用多个WORKDIR指令,后续命令如果参数是相对路径,则 会基于之前命令指定的路径
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
最终的输出pwd命令这 Dockerfile 将是/a/b/c
- EXPOSE
声明镜像内服务所监听的端口
格式为EXPOSE[...]
EXPOSE 指令通知 Docker 容器在运行时监听指定的网络端口。可以指定端口是侦听 TCP 还是UDP ,如果未指定协议,则默认值为 TCP 。
EXPOSE 22 80 8443
默认情况下, EXPOSE 假定为 TCP 。还可以指定 UDP :
EXPOSE 80/udp
要同时在TCP和UDP上公开,请包括以下两行:
EXPOSE 80/tcp
EXPOSE 80/udp
无论EXPOSE
设置如何,都可以在运行时使用该-p
标志覆盖它们。例如
docker run -p 80:80/tcp
# 4.1.3 DockerFile创建镜像
编写完成Dockerfile之后,可以通过docker build命令来创建镜像。
基本的格式为docker build[选项]内容路径,该命令将读取指定路径下(包括子目录)的Dockerfile,并将该路径下的所有内容发送给Docker服务端,由服务端来创建镜像
如果使用非内容路径下的Dockerfile,可以通过-f选项来指定其路径
$ docker build -f /path/to/a/Dockerfile .
要指定生成镜像的标签信息,可以使用-t选项
docker build -t lagou/ubuntu:v1 .
docker build 最后的 . 号,其实是在指定镜像构建过程中的上下文环境的目录
镜像管理
Docker镜像由一系列层组成。每层代表镜像的Dockerfile中的一条指令。除最后一层外的每一层都是只读的。考虑以下Dockerfile:
FROM ubuntu:18.04
COPY . /app
RUN make /app
CMD python /app/app.py
该Dockerfile包含四个命令,每个命令创建一个层。
该 FROM语句首先从ubuntu:18.04镜像创建只读层。
该COPY命令从Docker客户端的当前目录添加一些文件。
该RUN命令使用命令构建您的应用程序make。
最后一层指定在容器中运行什么命令。 运行镜像并生成容器时,可以在基础层之上添加一个新的可写层("容器层")。对运行中的容器所做的所有更改(例如写入新文件,修改现有文件和删除文件)都将写入此可写容器层。
容器和镜像之间的主要区别是可写顶层。在容器中添加新数据或修改现有数据的所有写操作都存储在此可写层中。删除容器后,可写层也会被删除。基础镜像保持不变。
因为每个容器都有其自己的可写容器层,并且所有更改都存储在该容器层中,所以多个容器可以共享对同一基础镜像的访问,但具有自己的数据状态。下图显示了多个共享相同Ubuntu 18.04镜像的容器
查看镜像的分层信息
docker history 镜像ID
磁盘上容器大小
要查看正在运行的容器的大致大小,可以使用以下 docker ps -s 命令。有两个不同的列与大小有关。
size :用于每个容器的可写层的数据量(在磁盘上)。
virtual size :容器使用的只读图像数据加上容器的可写层使用的数据量size。多个容器可以共享部分或全部只读图像数据。从同一图像开始的两个容器共享100%的只读数据,而具有不同图像的两个容器(具有相同的层)共享这些公共层。因此,您不能只对虚拟大小进行总计。这高估了总磁盘使用量,可能是一笔不小的数目。
我们可以通过Docker仓库来传输我们的镜像,也可以通过文件模式
docker save 镜像ID -o xxxx.tar 或(docker save xxxx > xxxx.tar)
docker load -i xxxx.tar 或docker (docker load < xxxx.tar)
docker diff 容器ID
docker commit 容器ID svendowideit/testimage:version4 # 直接保存容器
docker commit --change='CMD ["apachectl", "-DFOREGROUND"]' -c "EXPOSE 80" 容器ID
svendowideit/testimage:version4 # 将正在运行的容器添加几个层之后再保存】
# 4.1.4 DockerFile模版
编写建议:
从适当的基础镜像开始。例如,如果需要JDK镜像基于正式 openjdk 镜像
使用多阶段构建。例如,可以使用该 maven 镜像来构建Java应用程序,然后将其重置为该 tomcat镜像并将Java构件复制到正确的位置以部署我们的应用程序,所有这些操作都在同一 Dockerfile中。这意味着的最终镜像不包括构建所引入的所有库和依赖项,而仅包括运行它们所需的工件和环境。
通过最小化Dockerfile中 RUN 单独命令的数量来减少镜像中的层数。可以通过将多个命令合并为RUN 一行并使用Shell的机制将它们组合在一起来实现此目的。考虑以下两个片段。第一层在镜像中创建两层,而第二层仅创建一层。
#在镜像中创建两层
RUN apt-get -y update
RUN apt-get install -y python
#在镜像中创建一层
RUN apt-get -y update && apt-get install -y python
- 如果有多个具有很多共同点的图像,请考虑使用共享的组件创建自己的基础镜像,Docker只需要加载一次公共层,然后将它们缓存。这意味着我们的派生镜像将更有效地使用Docker主机上的内存,并更快地加载。
# 4.1.4.1 CentOS镜像模版
得益于Docker镜像的分层技术,我们尽量将不变的构建指令放在Dockerfile较靠前的位置,常变的放在Dockerfile较下层的位置,这样后续重新构建镜像是就可以使用之前构建镜像的只读层版本了 之所以yum分了很多个 RUN 指令,是因为可能后期仍会安装部分功能,而后期安装的 RUN 指令,可以用之前的只读层,这样可以减少后续构建镜像的时间
注意:如果使用虚拟机,不要用NAT网络 要用网桥
dockerfile-centos
# 依据哪个镜像创建
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"]
构建命令:
docker build -f dockerfile-centos -t lagou/centos/7.6/centos .
docker run --rm -it lagou/centos/7.6/centos
# 4.1.4.2 JDK镜像模版
dockerfile-jdk
FROM lagou/centos/7.6/centos
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"]
构建命令:
#下载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 lagou/centos/7.6/jdk/1.8/jdk .
# 4.1.4.3 Tomcat镜像模版
dockerfile-tomcat
FROM lagou/centos/7.6/jdk/1.8/jdk
ENV TOMCAT_HOME="/apps/tomcat"
ENV PATH=${TOMCAT_HOME}/bin:$PATH
RUN yum install -y gcc make expat-devel apr-devel; yum clean all ; rm -rf /var/cache/yum/*
ADD ./apr-1.6.5.tar.gz /root/
RUN cd /root/apr-1.6.5 && ./configure && make && make install && rm -rf /root/apr*
ADD ./apr-util-1.6.1.tar.gz /root/
RUN cd /root/apr-util-1.6.1 && ./configure --with-apr=/usr/local/apr && make && make install && rm -rf /root/apr*
ADD ./apache-tomcat-7.0.93.tar.gz /data/apps/
RUN ln -s /apps/apache-tomcat-7.0.93 /apps/tomcat
RUN cd /apps/tomcat/bin; tar xf tomcat-native.tar.gz; cd /apps/tomcat/bin/tomcat-native-1.2.21-src/native && \
./configure --with-apr=/usr/local/apr && make && make install
CMD ["/bin/bash"]
构建命令:
#下载包
wget http://pkgs-linux.cvimer.com/apr/1.6.5/apr-1.6.5.tar.gz
wget http://pkgs-linux.cvimer.com/apr/1.6.1/apr-util-1.6.1.tar.gz
wget https://archive.apache.org/dist/tomcat/tomcat-7/v7.0.93/bin/apache-tomcat-7.0.93.tar.gz
docker build -f dockerfile-tomcat -t lagou/centos/7.6/jdk/1.8/tomcat/7.0.9/tomcat .
运行
docker run --rm -it --network host lagou/centos/7.6/jdk/1.8/tomcat/7.0.9/tomcat
#进入Docker
[root@teacher1 /]# cd apps
[root@teacher1 apps]# ls
apache-tomcat-7.0.93 jdk jdk1.8.0_251 tomcat
[root@teacher1 apps]# cd tomcat/
[root@teacher1 tomcat]# cd bin/
[root@teacher1 bin]# ./startup.sh
Using CATALINA_BASE: /apps/tomcat
Using CATALINA_HOME: /apps/tomcat
Using CATALINA_TMPDIR: /apps/tomcat/temp
Using JRE_HOME: /apps/jdk
Using CLASSPATH: /apps/tomcat/bin/bootstrap.jar:/apps/tomcat/bin/tomcat-juli.jar
Tomcat started.
访问:
# 4.2 Docker数据持久化
- 创建一个卷,待后边使用
docker volume create test_volume
- 分别启动2个容器挂在上卷,
# 在2个终端窗口启动2个容器
docker run -it --rm -v test_volume:/test nginx:latest /bin/bash
docker run -it --rm -v test_volume:/test nginx:latest /bin/bash
cd /test;
touch a.txt
ls /test
# 在两个容器中我们均可以看到我们创建的文件,这样我们就可以做到了在多个容器之间实现数据共享
挂载在容器 /test 目录内创建。 Docker 不支持容器内安装点的相对路径。 多个容器可以在同一时间段内使用相同的卷。如果两个容器需要访问共享数据,这将很有用。例如,如果一个容器写入而另一个容器读取数据。 卷名 在驱动程序test必须唯一。这意味着我们不能将相同的卷名与两个不同的驱动程序一起使用。 如果我们指定了当前test_volume程序上已在使用的卷名,则Docker会假定我们要重用现有卷,并且不会返回错误。如果开始无 test_volume 则会创建这个卷
当然除了使用卷,也可以使用将宿主机的文件映射到容器的卷,命令类似,只不过不用提前创建卷,而且数据会映射到宿主机上
docker run -it --rm -v /root/test_volume:/test centos /bin/bash
注意如果宿主机上的目录可以不存在,会在启动容器的时候创建
# 5 Docker运维管理
# 5.1 Swarm集群管理
docker swarm是docker官方提供的一套容器编排系统,是Docker公司推出的官方容器集群平台。基于Go语言实现。它的架构如下:
# 5.1.1 Swarm的核心概念
- 集群
一个集群由多个 Docker 主机组成,这些Docker主机以集群模式运行,并充当管理者(用于管理成员资格和委派)和工作人员(运行集群服务)
与独立容器相比,集群服务的主要优势之一是,可以修改服务的配置,包括它所连接的网络和卷,而无需手动重新启动服务。
独立容器和集群服务之间的主要区别在于,只有集群管理器可以管理集群,而独立容器可以在任何守护程序上启动。
- 节点
swarm是一系列节点的集合,而节点可以是一台裸机或者一台虚拟机。一个节点能扮演一个或者两个角色manager或者worker
manager节点
Docker Swarm集群需要至少一个manager节点,节点之间使用 进行协同工作。 通常,第一个启用docker swarm的节点将成为leader,后来加入的都是follower。
当前的leader如果挂掉,剩余的节点将重新选举出一个新的leader。 每一个manager都有一个完整的当前集群状态的副本,可以保证manager的高可用
worker节点
worker节点是运行实际应用服务的容器所在的地方。理论上,一个manager节点也能同时成为worker节点,但在生产环境中,我们不建议这样做。 worker节点之间,通过 control plane 进行通信,这种通信使用 gossip 协议,并且是异步的
- 服务和任务
集群中的stacks, services, tasks的关系,如下图:
services(服务)
swarm service是一个抽象的概念,它只是一个对运行在swarm集群上的应用服务,所期望状态的描述。它就像一个描述了下面物品的清单列表一样:
服务名称
使用哪个镜像来创建容器
要运行多少个副本
服务的容器要连接到哪个网络上
应该映射哪些端口
task(任务)
在Docker Swarm中,task是一个部署的最小单元,task与容器是一对一的关系
stack(栈)
stack是描述一系列相关services的集合。我们通过在一个YAML文件中来定义一个stack
- 负载均衡
集群管理器使用入口负载平衡将要从集群外部获取的服务公开给集群。
集群管理器可以自动为服务分配一个已发布端口,也可以为该服务配置一个已发布端口。
可以指定任何未使用的端口。如果未指定端口,则集群管理器会为服务分配 30000-32767 范围内的端口。
集群模式具有一个内部DNS组件,该组件自动为群集中的每个服务分配一个DNS条目。
集群管理器使用内部负载平衡根据服务的DNS名称在群集内的服务之间分配请求。
# 5.1.2 Swarm安装
对于Docker 1.12+版本,Swarm相关命令已经原生嵌入到了Docker Engine中
1、下载镜像
# 拉取镜像
docker pull swarm
2、查看版本
# 查看版本
docker run --rm swarm -v
swarm version 1.2.9 (527a849)
# 5.1.3 Swarm集群
1、创建新集群
[root@teacher1 com-test]# docker swarm init --advertise-addr 172.16.86.47
Swarm initialized: current node (i7lk1f63no4tl7o6depa1egha) is now a manager.
To add a worker to this swarm, run the following command:
[root@teacher1 com-test]# docker swarm join --token SWMTKN-1-60etl768pzoibxuqlpeuyyp73jjwwknbc2yvlfxpzwc568vr8o-e7y60md3jxd8u4mkxzug13xeb 172.16.86.47:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow
the instructions.
# 将节点强制驱除集群
docker swarm leave --force
上面命令执行后,该机器自动加入到swarm集群。这个会创建一个集群token,获取全球唯一的token,作为集群唯一标识。后续将其他节点加入集群都会用到这个token值。
其中,--advertise-addr
参数表示其它swarm中的worker节点使用此ip地址与manager联系。命令的输出包含了其它节点如何加入集群的命令。
2、查看集群状态和节点信息
#查看集群状态
$ docker info
Containers: 2
Running: 0
Paused: 0
Stopped: 2
...snip...
Swarm: active
NodeID: i7lk1f63no4tl7o6depa1egha
Is Manager: true
ClusterID: v9xgock39u6ruod3q4u97zjsb
Managers: 1
Nodes: 1
...snip...
#查看节点信息
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
i7lk1f63no4tl7o6depa1egha * teacher1 Ready Active Leader 19.03.12
3、添加工作节点到集群
创建了一个集群与管理器节点,就可以添加工作节点。
在工作节点172.16.206.25上安装docker和docker swarm
[root@hadoop5 teacher]# docker swarm join --token SWMTKN-1-60etl768pzoibxuqlpeuyyp73jjwwknbc2yvlfxpzwc568vr8o-e7y60md3jxd8u4mkxzug13xeb 172.16.86.47:2377
This node joined a swarm as a worker.
# 将节点强制驱除集群
docker swarm leave --force
如果忘记了token的值,在管理节点172.16.86.47上
#在管理者节点上写
[root@teacher1 com-test]# docker swarm join-token manager
To add a manager to this swarm, run the following command:
[root@teacher1 com-test]# docker swarm join --token SWMTKN-1-0l5r5oia275bn2rtd098o9vyan73jf5cx7ryrr8gq7bwj7x9wc-d2nt6ls01ff6hnfsnvmcjpgqu 172.16.86.47:2377
[root@teacher1 com-test]# docker swarm join-token worker
To add a worker to this swarm, run the following command:
[root@teacher1 com-test]# docker swarm join --token SWMTKN-1-0l5r5oia275bn2rtd098o9vyan73jf5cx7ryrr8gq7bwj7x9wc-3plqhvbn8086h1g7ubse0nnmy 172.16.86.47:2377
在管理节点172.16.86.47上
#查看节点信息
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
jp8nq2d42agepijggirptiie2 hadoop5 Ready Active 19.03.12
i7lk1f63no4tl7o6depa1egha * teacher1 Ready Active Leader 19.03.12
4、发布服务到集群
在管理节点172.16.86.47上
[root@teacher1 teacher]# docker service create -p 80:80 --replicas 2 --name
nginx1 nginx
mp0mt313oevgp3jtyuuudeul5
overall progress: 2 out of 2 tasks
1/2: running
2/2: running
verify: Service converged
访问:http://113.31.119.154
-p :端口映射
--replicas:运行实例个数
--name:服务名
nginx : 镜像
查看发布的服务
#列表查询
[root@teacher1 teacher]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
mp0mt313oevg nginx1 replicated 2/2 nginx:latest *:80->80/tcp
#详细查询
[root@teacher1 teacher]# docker service inspect --pretty nginx1
ID: mp0mt313oevgp3jtyuuudeul5
Name: nginx1
Service Mode: Replicated
Replicas: 2
Placement:
UpdateConfig:
Parallelism: 1
On failure: pause
Monitoring Period: 5s
Max failure ratio: 0
Update order: stop-first
RollbackConfig:
Parallelism: 1
On failure: pause
Monitoring Period: 5s
Max failure ratio: 0
Rollback order: stop-first
ContainerSpec:
Image:
nginx:latest@sha256:36b74457bccb56fbf8b05f79c85569501b721d4db813b684391d63e02287c0b2
Init: false
Resources:
Endpoint Mode: vip
Ports:
PublishedPort = 80
Protocol = tcp
TargetPort = 80
PublishMode = ingress
查看哪些节点正在运行服务
[root@teacher1 teacher]# docker service ps nginx1
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
iln3bhgwn0ux nginx1.1 nginx:latest teacher1 Running Running about a minute ago
1iwz3zbxkdtz nginx1.2 nginx:latest hadoop5 Running Running about a minute ago
以上命令均在管理节点上运行
停止并删除发布的服务
[root@teacher1 teacher]# docker service rm nginx1
nginx1
查看哪些节点正在运行服务
在工作节点上运行的命令
[root@hadoop5 teacher]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d7b9c35bd5a5 nginx:latest "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 80/tcp nginx1.1.mfr5dyukmihvw9qc3nf7lchkq
5、扩展一个或多个服务
[root@teacher1 teacher]# docker service scale nginx1=3
nginx1 scaled to 3
overall progress: 3 out of 3 tasks
1/3: running
2/3: running
3/3: running
verify: Service converged
[root@teacher1 teacher]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
0tnw762m7f0r nginx1 replicated 3/3 nginx:latest *:80->80/tcp
[root@teacher1 teacher]# docker service ps nginx1
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
mfr5dyukmihv nginx1.1 nginx:latest hadoop5 Running Running 25 minutes ago
j0xef7nnvcdu nginx1.2 nginx:latest teacher1 Running Running about a minute ago
irxjitlxpoug nginx1.3 nginx:latest teacher1 Running
6、更新服务
docker service update --publish-rm 80:80 --publish-add 88:80 nginx1
访问:http://113.31.119.154:88/
还可以更新很多 具体可加 --help查询
7、节点管理
命令名称 | 说明 |
---|---|
docker node demote | 从群集(swarm)管理器中降级一个或多个节点 |
docker node inspect | 显示一个或多个节点的详细信息 |
docker node ls | 列出群集(swarm)中的节点 |
docker node promote | 从群集(swarm)管理器中升级一个或多个节点 |
docker node ps | 列出在一个或多个节点上运行的任务,默认为当前节点 |
docker node rm | 从群集(swarm)删除一个或多个节点 |
docker node update | 更新一个节点 |
#给hadoop5添加worker1标签
[root@teacher1 teacher]# docker node update --label-add worker1 hadoop5
hadoop5
#给hadoop5节点停用
[root@teacher1 teacher]# docker node update --availability drain hadoop5
hadoop5
#hadoop5节点停止docker
systemctl stop docker
#删除节点
[root@teacher1 teacher]# docker node rm hadoop5
hadoop5
# 5.2 Docker Compose
通过前面的讲解我们知道使用一个Dockerfile模板文件,可以很方便地定义一个单独的应用容器。然而,在日常工作中, 经常会碰到需要多个容器相互配合来完成某项任务的情况。例如要实现一个Web项目,除了Web服务容器本身,往往还需要再加上后端的数据库服务容器,甚至还包括负载均衡容器等。
Compose定位是“定义和运行多个Docker容器的应用”,它允许用户通过一个单独的docker-compose.yml模板文件(YAML格式)来定义一组相关联的应用容器为一个项目(project)。
# 5.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来进行编排管理。
# 5.2.2 Compose的安装
安装Docker Compose
- 运行以下命令以下载Docker Compose的当前稳定版本:
#$(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
要安装其他版本的Compose,请替换1.26.0 为要使用的Compose版本。
- 将可执行权限应用于二进制文件:
chmod +x /usr/local/bin/docker-compose
- 添加到环境中
#ln -s : 软链接
ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
- 测试安装。
docker-compose --version
卸载Docker Compose
rm /usr/local/bin/docker-compose
# 5.2.3 docker-compose.yml
模板文件是使用Compose的核心,默认的模板文件名称为docker-compose.yml,格式为YAML格式。
version: '2.0' #版本2.0
services: #定义服务
nginx: #Nginx容器
image: nginx:1.18.0 #Nginx:latest 镜像 先拉镜像 指定镜像的版本
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 #Nginx:latest 镜像 先拉镜像 指定镜像的版本
**ports:**端口映射 HOST:CONTAINER
ports:
- 8000:80 #映射端口
build: 指定dockerfile,Compose 将会利用它自动构建这个镜像,然后使用这个镜像启动服务容器
构建自己的应用
build: /path/dockerfilename
build:./path/dockerfilename
command:使用 command 可以覆盖容器启动后默认执行的命令
command:echo $HOME
command:[ "echo", "$HOME" ]
command: redis-server /usr/local/etc/redis/redis.conf #先创建目录和文件
**depends_on:**确定容器的启动顺序
depends_on:
- db #服务名
- redis
**environment:**设置镜像变量
environment:
RACK_ENV: development
SHOW: 'true'
SESSION_SECRET:
**volumes:**挂载一个目录或者一个已存在的数据卷容器,可以直接使用 [HOST:CONTAINER]
这样的格式,或者使用 [HOST:CONTAINER:ro] 这样的格式,后者对于容器来说,数据卷是只读的,这样可以有效保护宿主机的文件系统
Compose的数据卷指定路径可以是绝对路径或相对路径
volumes: #挂载数据
- /data/nginx/conf.d:/etc/nginx/conf.d # 如果不写将使用默认
- /data/log:/var/log/nginx
- /data/www:/var/www
注意:如果目录或文件不存在将启动不成功,所以要先创建好目录或文件
network_mode:网络模式
network_mode: bridge network_mode: host network_mode: none network_mode: service:
[service name] network_mode: container:[container name/id]
默认bridge
[root@teacher1 com-test]# docker network ls
NETWORK ID NAME DRIVER SCOPE
31289c518ea1 bridge bridge local
bcb2529f97ae com-test_default bridge local
db2ff5be4e36 host host local
e8aa24d5f78f none null local
加入该配置后,不新增网络
[root@teacher1 com-test]# docker network ls
NETWORK ID NAME DRIVER SCOPE
31289c518ea1 bridge bridge local
db2ff5be4e36 host host local
e8aa24d5f78f none null local
**networks:**定义网络
# 默认网络
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
案例:
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
# 5.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
up以后没有down 可以运行start
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:在一个服务上执行一个命令
docker-compose run nginx bash
down 停止并删除容器
docker-compose down
-v : 可以删除容器使用的数据
# 5.2.5 与Swarm一起使用Compose
我们知道使用 docker-compose.yml可以来一次配置、启动多个容器,在 Swarm 集群中也可以使用compose 文件 (docker-compose.yml) 来配置、启动多个服务。
使用 docker service create 一次只能部署一个服务,使用 docker-compose.yml 我们可以一次启动多个关联的服务。
打开前面的docker-compose.yml (有多个容器)修改:
version: "3.0"
deploy:
mode: replicated
replicas: 2
去掉不支持的:
restart: always
container_name: nginx
结果:
version: "3.0"
services:
nginx:
image: nginx:1.18.0
ports:
- 80:80
- 443:443
volumes:
- /docker/nginx/log:/var/log/nginx
- /docker/nginx/www:/etc/nginx/html
- /etc/letsencrypt:/etc/letsencrypt
deploy:
mode: replicated
replicas: 2
mysql:
image: mysql:5.7.30
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"
deploy:
mode: replicated
replicas: 2
redis:
image: redis:5.0.9
environment:
- TZ=Asia/beijing
ports:
- 6379:6379
#主备都存在
volumes:
- /docker/redis/data:/data
deploy:
mode: replicated
replicas: 2
运行:
$ docker stack deploy -c docker-compose.yml web
Creating network web_default
Creating service web_redis
Creating service web_nginx
Creating service web_mysql
查看服务:
$ docker stack services web
ID NAME MODE REPLICAS IMAGE PORTS
gkjsbzbbkqjp web_nginx replicated 2/2 nginx:1.18.0 *:80->80/tcp, *:443->443/tcp
qbfpbiwlfuot web_mysql replicated 2/2 mysql:5.7.30 *:13306->3306/tcp
wb4eixvr2l38 web_redis replicated 2/2 redis:5.0.9 *:6379->6379/tcp
删除服务:
$ docker stack down web
Removing service web_mysql
Removing service web_nginx
Removing service web_redis
Removing network web_default
# 5.3 配置私有仓库(Harbor)
Harbor(港湾),是一个用于存储和分发Docker镜像的企业级Registry服务器
以前的镜像私有仓库采用官方的Docker Registry,不便于管理镜像。
Harbor是由VMWare在Docker Registry的基础之上进行了二次封装,加进去了很多额外程序,而且提供了一个web界面。其主要组件如下图:
主要组件包括:
proxy,他是一个nginx前端代理,主要是分发前端页面ui访问和镜像上传和下载流量
ui提供了一个web管理页面,当然还包括了一个前端页面和后端API,底层使用mysql数据库
registry是镜像仓库,负责存储镜像文件,当镜像上传完毕后通过hook通知ui创建repository
adminserver是系统的配置管理中心附带检查存储用量,ui和jobserver启动时候需要加载adminserver的配置,jobsevice是负责镜像复制工作的,他和registry通信,从一个registry pull镜像然后push到另一个registry,并记录job_log
log是日志汇总组件,通过docker的log-driver把日志汇总到一起。
# 5.3.1 Harbor安装
1、下载Harbor
wget http://kubernetes.cvimer.com/harbor-offline-installer-v1.10.3.tgz
2、解压
tar xf harbor-offline-installer-v1.10.3.tgz
cd harbor
#将镜像加载到docker中
docker load -i harbor.v1.10.3.tar.gz
# 5.3.2 Harbor配置
vi harbor.yml
公开访问
#采用域名
hostname: lgharbor.com
http:
port: 80
# 安全访问
#https:
# port: 443
# certificate: /etc/docker/certs.d/harbor.lgedu.com:8443/lgedu.com.cert
# private_key: /etc/docker/certs.d/harbor.lgedu.com:8443/lgedu.com.key
harbor_admin_password: Harbor12345
database:
password: root123
max_idle_conns: 50
max_open_conns: 100
#设置数据库路径
data_volume: /data/harbor
clair:
updaters_interval: 12
jobservice:
max_job_workers: 100
notification:
webhook_job_max_retry: 100
chart:
absolute_url: disabled
log:
level: info
local:
rotate_count: 50
rotate_size: 200M
location: /var/log/harbor
_version: 1.10.0
proxy:
http_proxy:
https_proxy:
no_proxy:
components:
- core
- jobservice
- clair
登录名:admin
密码:Harbor12345
# 5.3.3 Harbor启动和停止
1、运行prepare脚本
Harbor将 nginx 实例用作所有服务的反向代理。您可以使用 prepare 脚本来配置 nginx 为使用HTTP和HTTPS
./prepare
2、利用docker-compose启动和停止Harbor
#启动
docker-compose up -d
#关闭
docker-compose down
#强制清理
docker system prune -a -f
#查看
docker-compose ps
harbor-core /harbor/harbor_core Up (health: starting)
harbor-db /docker-entrypoint.sh Up (health: starting)
5432/tcp
harbor-jobservice /harbor/harbor_jobservice Up (health: starting)
...
harbor-log /bin/sh -c /usr/local/bin/ Up (health: starting)
127.0.0.1:1514->10514/tcp
...
harbor-portal nginx -g daemon off; Up (health: starting)
8080/tcp
nginx nginx -g daemon off; Up (health: starting)
0.0.0.0:80->8080/tcp
redis redis-server Up (health: starting)
6379/tcp
/etc/redis.conf
registry /home/harbor/entrypoint.sh Up (health: starting)
5000/tcp
registryctl /home/harbor/start.sh Up (health: starting)
# 5.3.4 Harbor访问和登录
1、配置hosts文件,做IP与域名的映射
vim /etc/hosts
172.16.86.47 lgharbor.com
2、打开浏览器,然后输入 http://lgharbor.com 。显示Harbor界面。
3、修改daemon.json,默认http私有仓库不能访问,设置后才可以
vim /etc/docker/daemon.json
{
"insecure-registries" : ["lgharbor.com"]
}
# 重启Docker进程
systemctl restart docker
# 重启Harbor
docker-compose up -d
3、从Docker客户端登录Harbor
docker login lgharbor.com
username:admin
password:Harbor12345
# 5.3.5 推送镜像到Harbor
1、为要推送的镜像打tag
docker tag centos:7.6.1810 lgharbor.com/lgedu/centos:7.6.1810
lgharbor.com:域名
lgedu:项目名,在Harbor中创建
2、推送镜像到Harbor中
docker push lgharbor.com/lgedu/centos:7.6.1810
# 5.3.6 生产环境中使用
1、生成CA证书
#生成CA证书私钥
openssl genrsa -out ca.key 4096
#生成CA证书
openssl req -x509 -new -nodes -sha512 -days 3650 \
-subj "/C=CN/ST=Beijing/L=Beijing/O=lgharbor/OU=Personal/CN=lgharbor.com" \
-key ca.key \
-out ca.crt
2、生成服务器证书
证书通常包含一个 .crt 文件和一个 .key 文件
生成私钥
openssl genrsa -out lgharbor.com.key 4096
生成证书签名请求(CSR)
openssl req -sha512 -new \
-subj "/C=CN/ST=Beijing/L=Beijing/O=lgharbor/OU=Personal/CN=lgharbor.com" \
-key lgharbor.com.key \
-out lgharbor.com.csr
生成一个x509 v3扩展文件
cat > v3.ext <<-EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1=*.lgharbor.com
DNS.2=lgharbor.com
EOF
使用该v3.ext文件为您的Harbor主机生成证书
openssl x509 -req -sha512 -days 3650 \
-extfile v3.ext \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-in lgharbor.com.csr \
-out lgharbor.com.crt
3、向Harbor和Docker提供证书
生成后ca.crt,lgharbor.com.crt和lgharbor.com.key文件,必须将它们提供给Harbor和Docker,和重新配置Harbor使用它们。
将服务器证书和密钥复制到Harbor主机上的certficates文件夹中
mkdir -p /data/cert
cp lgharbor.com.crt /data/cert/
cp lgharbor.com.key /data/cert/
转换lgharbor.com.crt为lgharbor.com.cert,供Docker使用
openssl x509 -inform PEM -in lgharbor.com.crt -out lgharbor.com.cert
将服务器证书,密钥和CA文件复制到Harbor主机上的Docker证书文件夹中。必须首先创建适当的文件夹
mkdir -p /etc/docker/certs.d/lgharbor.com:443/
cp lgharbor.com.cert /etc/docker/certs.d/lgharbor.com:443/
cp lgharbor.com.key /etc/docker/certs.d/lgharbor.com:443/
cp ca.crt /etc/docker/certs.d/lgharbor.com:443/
重新启动Docker Engine
systemctl restart docker
4、修改Harbor配置
开启https
vim harbor.yml
https:
port: 443
certificate: /etc/docker/certs.d/lgharbor.com:443/lgharbor.com.cert
private_key: /etc/docker/certs.d/lgharbor.com:443/lgharbor.com.key
5、启动harbor
./prepare
#启动
docker-compose up -d
#关闭
docker-compose down
#查看
docker-compose ps
6、从Docker客户端登录Harbor
cp ca.crt /etc/pki/ca-trust/source/anchors/
# 更新证书配置
update-ca-trust
# 重启Docker进程
systemctl restart docker
docker-compose down
docker-compose up -d
7、登录Harbor
docker login lgharbor.com:443