Docker
[TOC]
Docker 仓库:https://hub.docker.com/explore/
Docker简介
一次编译,到处运行 JVM
hypervisor:一种运行在物理服务器和操作系统之间的中间层软件,可以允许多个操作系统和应用共享一套基础物理硬件。可以将hypervisor看做是虚拟环境中的“元”操作系统,可以协调访问服务器上的所有物理设备和虚拟机,所以又称为虚拟机监视器(virtual machine monitor)。
阿里云、腾讯云等的架构
虚拟机与Docker的区别:
传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。
对比
特性 | 容器 | 虚拟机 |
---|---|---|
启动 | 秒级 | 分钟级 |
硬盘使用 | 一般为MB | 一般为GB |
性能 | 接近原生 | 弱于原生 |
系统支持量 | 单机支持上千个容器 | 一般为几十个 |
虚拟机里无法运行虚拟机
Docker引擎
Docker 引擎是一个包含以下主要组件的客户端服务器应用程序
- 一种服务器,它是一种称为守护进程并且长时间运行的程序
- REST API用于指定程序可以用来与守护进程通信的接口,并指示它做什么
- 一个命令行界面(CLI)工具的客户端
- docker image
- docker container
Docker 使用客户端-服务器 (C/S) 架构模式,使用远程 API 来管理和创建 Docker 容器。
Docker 容器通过 Docker 镜像来创建,容器与镜像的关系类似于面向对象编程中的对象与类,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
标题 | 说明 |
---|---|
镜像(Images) | Docker镜像是用于创建Docker容器的模板 |
容器(Container) | 容器是独立运行的一个或一组应用 |
客户端(Client) | Docker客户端通过命令行或者其他工具使用Docker API与Docker的守护进程通信 |
主机(Host) | 一个物理或者虚拟的机器用于执行Docker守护进程和容器 |
仓库(Registry) | Docker仓库用来保存镜像,可以理解为代码控制中的代码仓库。Docker Hub(https://hub.docker.com) 提供了庞大的镜像集合供使用。 |
Docker Machine | Docker Machine是一个简化Docker安装的命令行工具,通过一个简单的命令行即可在相应的平台上安装Docker,比如VirtualBox、 Digital Ocean、Microsoft Azure。 |
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。因此容器可以拥有自己的 root
文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。
前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为容器存储层。
容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。
按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用 数据卷(Volume)
、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。
一个 Docker Registry 中可以包含多个仓库(Repository
);每个仓库可以包含多个标签(Tag
);每个标签对应一个镜像。
学习公有仓库怎么用,私有仓库怎么建。
Docker镜像加速器
对于使用 systemd 的系统,请在 /etc/docker/daemon.json
中写入如下内容(如果文件不存在请新建该文件)
{
"registry-mirrors": [
"https://registry.docker-cn.com"
]
}
之后重新启动服务
sudo systemctl daemon-reload
sudo systemctl restart docker
检查生效,执行docker info
,看是否有以下内容
Registry Mirrors:
https://registry.docker-cn.com/
Ubuntu安装Docker
使用脚本自动安装,在测试开发环境中Docker官方为了简化安装流程,提供了一套便捷的安装脚本:
curl -fsSL get.docker.com -o get-docker.sh
# 可能会出现 404 错误,请移步下面的特别说明
# sudo sh get-docker.sh --mirror Aliyun #阿里云目前有问题
sudo sh get-docker.sh --mirror AzureChinaCloud
执行这个命令后,脚本就会自动将一切准备工作做好,并且把Docker CE的Edge版本安装在系统中。
启动Docker CE
sudo systemctl enable docker
sudo systemctl start docker
建立用户组:
默认情况下,docker
命令会使用 Unix socket 与 Docker 引擎通讯。而只有 root
用户和 docker
组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 root
用户。因此,更好地做法是将需要使用 docker
的用户加入 docker
用户组。
- 建立
docker
组
sudo groupadd docker
- 将当前用户加入docker组
sudo usermod -aG docker $USER
使用Docker镜像
获取、列出、删除镜像
docker pull
:从Docker镜像仓库获取镜像,命令格式如下:
docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
- Docker 镜像仓库地址:地址的格式一般是
<域名/IP>[:端口号]
。默认地址是 Docker Hub。 - 仓库名:如之前所说,这里的仓库名是两段式名称,即
<用户名>/<软件名>
。对于 Docker Hub,如果不给出用户名,则默认为library
,也就是官方镜像。
$ docker pull ubuntu:16.04
# 上面的命令中没有给出 Docker 镜像仓库地址,因此将会从 Docker Hub 获取镜像。而镜像名称是
# ubuntu:16.04,因此将会获取官方镜像 library/ubuntu 仓库中标签为 16.04 的镜像。
$ docker image ls # 显示顶层镜像
$ docker image ls -a # 显示所有镜像(包括顶层镜像和中间镜像)
#无标签的镜像很多都是中间层镜像,是其它镜像所依赖的镜像。这些无标签镜像不应该删除,否则会导致上层镜像因为依赖丢失而出错
$ docker image ls ubuntu # 根据仓库名列出镜像
$ docker image ls ubuntu:16.04 # 列出某个特定的镜像
$ docker ps # 查看运行中的容器
$ docker ps -a # 查看所有容器
$ docker run -it --rm \ # 交换的方式运行容器
ubuntu:16.04 \
bash
$ docker rm 容器id # 删除容器
$ docker system df # 查看镜像、容器、数据卷所占用的空间
$ docker image ls -f dangling=true # 显示虚悬镜像
$ docker image prune # 删除虚悬镜像
$ docker image rm [选项] <镜像> # 删除镜像,<镜像> 可以是 镜像短 ID、镜像长 ID、镜像名 或者 镜像摘要
docker run
就是运行容器的命令,我们这里简要的说明一下上面用到的参数。
-
-it
:这是两个参数,一个是-i
:交互式操作,一个是-t
终端。我们这里打算进入bash
执行一些命令并查看返回结果,因此我们需要交互式终端。 -
--rm
:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动docker rm
。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用--rm
可以避免浪费空间。 -
ubuntu:16.04
:这是指用ubuntu:16.04
镜像为基础来启动容器。 -
bash
:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是bash
。
进入容器后,我们可以在 Shell 下操作,执行任何所需的命令。这里,我们执行了 cat /etc/os-release
,这是 Linux 常用的查看当前系统版本的命令,从返回的结果可以看到容器内是 Ubuntu 16.04.4 LTS
系统。
exit
退出容器
使用Dockerfile定制镜像
Dockerfile是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
$ docker pull tomcat
$ docker run -it tomcat bash
$ ls -al #ll命令不能用
$ cd webapps/
$ ls -al
$ cd ROOT/
$ ls -al
# tomcat下ll、vi命令不能用,cat、echo能使用
$ exit # 退出
$ docker run -p 8080:8080 tomcat
自己装的软件必须放到 usr/local/
目录下(国际惯例)
区分:
- 交互的方式启动容器
$ docker run -it tomcat bash
#交互式启动容器进行修改修改不保存
- 交互的方式进入容器
$ docker run -p 8080:8080 tomcat
# 再开一个命令行
$ docker exec -it <容器id> bash
$ cd webapps/Root/
$ echo "Hello Docker Tomcat" >> index.jsp
docker run :创建一个新的容器并运行一个命令
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
示例:Dockerfile定制tomcat
镜像
# 创建docker目录(存放文件)
cd /usr/local/
mkdir docker
cd docker/
mkdir tomcat
cd tomcat/
vi Dockerfile
编写Dockerfile脚本:
FROM tomcat
WORKDIR /usr/local/tomcat/webapps/ROOT/
RUN rm -fr *
RUN echo "Hello Docker" > /usr/local/tomcat/webapps/ROOT/index.html
- FROM 指定基础镜像(必备,必须是第一条指令)
- RUN 执行命令行命令
- shell格式:
RUN <命令>
,就像直接在命令行中输入的命令一样
- shell格式:
构建镜像:(在Dockerfile文件所在目录执行)
- 命令:
docker build [选项] <上下文路径/URL/->
docker build -t myshop .
# repository名为myshop
# 不带标签表示latest
# .表示当前目录,告诉命令Dockerfile在哪,这个 . 实际上是在指定上下文的目录,docker build 命令会将该目录下的内容打包交给 Docker 引擎以帮助构建镜像。
以交互方式进入容器:
docker run -it --rm myshop bash
启动myshop
docker run -p 8080:8080 myshop
Docker指令详解
示例:在docker部署一个项目
1.拷贝 myshop.zip -> tomcat/webapps/ROOT
2.修改访问端口号为80
FROM tomcat
WORKDIR usr/local/tomcat/webapps/ROOT/
RUN rm -fr * # 先把ROOT目录下的东西删除,以免冲突出错
# my-shop-wed-admin-1.0.0.SNAPSHOT.zip与Dockerfile在同一个目录下
COPY my-shop-wed-admin-1.0.0.SNAPSHOT.zip . # .指当前目录,即WORKDIR
RUN unzip my-shop-wed-admin-1.0.0.SNAPSHOT.zip
RUN rm -fr my-shop-wed-admin-1.0.0.SNAPSHOT.zip
docker build -t myshop
- WORKDIR 指定工作目录:
WORKDIR <工作目录路径>
(注意:每一个 RUN
都是启动一个容器、执行命令、然后提交存储层文件变更,如果需要改变以后各层的工作目录的位置,那么应该使用 WORKDIR
指令。)
RUN 启动容器1
cd app
RUN 启动容器2
镜像本身指定的工作目录中 # 可以通过docker run -it --rm tomcat bash 进入容器查看
# 当指定工作目录后,docker run -it --rm tomcat bash进入的即为WORKDIR
命令解释docker run -p 8080:8080 tomcat
- 8080:8080 即将宿主机的8080端口映射到容器的8080端口
重启容器:docker restart <容器>
操作Docker容器
容器是独立运行的一个或一组应用,以及它们的运行状态环境。
启动容器
启动容器有两种方式,一种是基于镜像新建一个容器并启动,另一个是将在终止状态(stopped)的容器重新启动。因为Docker的容器实在太轻量级了,很多时候用户都是随时删除和新建容器。
新建并启动容器
$ docker run -it --rm ubuntu:16.04 bash
当利用docker run
来创建容器时,Docker在后台运行的标准操作包括:
- 检查本地是否存在指定的镜像,不存在就从公有仓库下载
- 利用镜像创建并启动一个容器
- 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
- 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
- 从地址池配置一个ip地址给容器
- 执行用户指定的应用程序
- 执行完毕后容器被终止
启动已终止容器
可以利用 docker container start <容器>
命令,直接将一个已经终止的容器启动运行。
以交互的方式进入容器$ docker exec -it <容器id> bash
终止容器docker container stop <容器>
此外,docker container restart
命令会将一个运行态的容器终止,然后再重新启动它。
守护态运行
更多的时候,需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。此时,可以通过添加 -d
参数来实现。
$ docker run -d tomcat
使用-d
参数启动后会返回一个唯一的id,也可以通过docker container ls
命令来查看容器信息,和docker ps
输出信息一样。
要获取容器的输出信息,可以通过docker container logs <容器>
命令
注: 容器是否会长久运行,是和 docker run
指定的命令有关,和 -d
参数无关。
进入容器
在使用 -d
参数时,容器启动后会进入后台。
某些时候需要进入容器进行操作,包括使用 docker attach
命令或 docker exec
命令,推荐大家使用 docker exec
命令,原因会在下面说明。
docker exec
后边可以跟多个参数,这里主要说明 -i
-t
参数。
只用 -i
参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回。当 -i
-t
参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符。
从exec
命令的stdin 中 exit,不会导致容器的停止。这就是为什么推荐大家使用 docker exec
的原因。
删除容器
删除一个处于终止状态的容器:docker container rm <容器>
清理所有处于终止状态的容器:docker container prune
数据卷
Docker Container 可以认为是面向对象中的对象
- 对象一旦被销毁,数据就不存在了
- 容器一旦被销毁,则容器内的数据将一并被删除
- 服务器中的图片也会一并被删除
- 容器中的数据不是持久化状态的
数据卷是一个可供一个或多个容器使用的特殊目录,它绕过UFS(UNIX文件系统??),可以提供很多有用的特性:
- 数据卷可以在容器之间共享和重用
- 对数据卷的修改会立马生效
- 对数据卷的更新,不会影响镜像
- 数据卷默认会一直存在,即使容器被删除
使用示例:
cd /usr/local/docker/tomcat/
mkdir ROOT # 需要创建个ROOT目录来替换容器中的ROOT目录
cd ROOT/
vi index.html
# -v 后 左边的是宿主机目录,右边的是容器目录
docker run -p 8080:8080 --name tomcat -d -v /usr/local/docker/tomcat/ROOT/:/usr/local/tomcat/webapps/ROOT/ tomcat
docker ps
docker exec -it tomcat bash
cd webapps/ROOT/
ls -al # 发现使用的是宿主机目录下的文件
docker run -p 8081:8080 --name tomcat2 -d -v /usr/local/docker/tomcat/ROOT/:/usr/local/tomcat/webapps/ROOT/ tomcat # 贡共享数据卷
Docker 构建数据库
docker pull mysql # 会拉取最新版本
docker pull mysql:5.7.24 # 建议使用这个版本
# 运行容器
docker run -p 3306:3306 --name mysql \
-v /usr/local/docker/mysql/conf:/etc/mysql \
-v /usr/local/docker/mysql/logs:/var/log/mysql \
-v /usr/local/docker/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.7.24
# 最新版启动方式已经改变,上述为5.x版本的启动方式
docker exec -it mysql bash
命令参数:
-
-p 3306:3306
:将容器的3306端口映射到主机的3306端口 -
-v /usr/local/docker/mysql/conf:/etc/mysql
:将主机当前目录下的 conf 挂载到容器的 /etc/mysql -
-v /usr/local/docker/mysql/logs:/var/log/mysql
:将主机当前目录下的 logs 目录挂载到容器的 /var/log/mysql -
-v /usr/local/docker/mysql/data:/var/lib/mysql
:将主机当前目录下的 data 目录挂载到容器的 /var/lib/mysql -
-e MYSQL\_ROOT\_PASSWORD=123456
:初始化root用户的密码
将容器里的文件复制到宿主机
# 修改配置文件
docker run -p 3306:3306 --name mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.7.24
docker exec -it mysql bash
whereis mysql
cd /etc/mysql/conf.d/
cat mysqldump.cnf # 查看mysql容量
echo "max_allowed_packet=128M" >> mysqld.cnf # 修改mysql容量
cat mysqld.cnf # 查看修改
exit
docker restart mysql # 重启容器
# 将容器里的文件复制到宿主机
cd /usr/local/docker/mysql/conf
docker cp mysql:/etc/mysql . # 将容器为mysql的/etc/mysql文件夹拷贝到.当前目录下
mv *.* ..
cd ..
rm -fr mysql
docker stop <容器>
docker rm -f <容器>
# 运行容器
docker run -p 3306:3306 --name mysql \
-v /usr/local/docker/mysql/conf:/etc/mysql \
-v /usr/local/docker/mysql/logs:/var/log/mysql \
-v /usr/local/docker/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.7.24
项目的容器化部署
cd /usr/local/docker/tomcat/ROOT/
rm -fr index.html
cd ..
cp my-shop-web.admin-1.0.0-SNAPSHOT.zip ROOT/
cd /usr/local/docker/tomcat/ROOT/
ll
unzip my-shop-web.admin-1.0.0-SNAPSHOT.zip
rm -fr my-shop-web.admin-1.0.0-SNAPSHOT.zip
ll
cd ..
# 修改项目配置文件
cd ./ROOT/WEB-INF/classes
ll
vi myshop.properties
cd /usr/local/docker/tomcat/
docker run -p 8080:8080 --name myshop -v /usr/local/docker/tomcat/ROOT/:/usr/local/tomcat/webapps/ROOT/ -d tomcat
# 查看日志
docker logs myshop
# 监听日志
docker logs -f myshop
Docker 批量操作容器或镜像
$ docker container rm [OPTIONS] CONTAINER [CONTAINER...]
# 如果要删除的 container 还是运行状态,要先停止容器
$ docker containt stop ffe5e
$ docker container rm ffe5e
# 清理所有处于终止状态的容器
$ docker container prune
$ docker image rm [选项] <镜像1> [<镜像2> ...]
<镜像>
可以是 镜像短 ID
、镜像长 ID
、镜像名
或者 镜像摘要
。
批量获取 容器ID 和 镜像ID
# 批量获取 容器ID
$ docker container ls -a -q
# 批量获取 镜像ID
$ docker image ls -a -q
批量停止容器
$ docker container stop $(docker container ls -a -q)
批量删除容器和镜像
# 批量删除容器:
$ docker container rm $(docker container ls -a -q)
# 清理所有处于终止状态的容器
$ docker container prune
# 批量删除镜像:
$ docker image rm $(docker image ls -a -q)
自定义
将要删除的容器名写到一个文档,然后根据提供的名字,再结合 grep 、awk 等命令,就可以轻松地获取准确的 ID 号了。只要获取了 ID 号,就可以用上面的方法将它们删除。