开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第18天,点击查看活动详情
1、Docker 简介
Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余 项目。它基于 Google 公司推出的 Go 语言实现。 项目后来加入了 Linux 基金会,遵从了 Apache 2.0 协议,项目代码在 GitHub 上进行维护。 Docker 项目的目标是实现轻量级的操作系统虚拟化解决方案。 Docker 的基础是 Linux 容器(LXC)等技术。 (背景),云计算兴起后,服务器硬件扩展非常便利,软件服务部署成为了瓶颈,docker 趁势而兴。
2、为什么用 Docker
容器的启动可以在秒级实现,比传统的虚拟机方式要快得多 对系统资源的利用率很高,一台主机上可以同时运行数千个 Docker 容器 docker 的出现,让开发/测试/线上的环境部署,成为便利一条龙。
更快速的交付和部署
对开发和运维(devop)人员来说,最希望的就是一次创建或配置,可以在任意地方正 常运行。
开发者可以使用一个标准的镜像来构建一套开发容器,开发完成之后,运维人员可以直 接使用这个容器来部署代码。 Docker 可以快速创建容器,快速迭代应用程序,并让整个过 程全程可见,使团队中的其他成员更容易理解应用程序是如何创建和工作的。 Docker 容器 很轻很快!容器的启动时间是秒级的,大量地节约开发、测试、部署的时间。
更高效的虚拟化
Docker 容器的运行不需要额外的 hypervisor 支持,它是内核级的虚拟化,因此可以实 现更高的性能和效率。
更轻松的迁移和扩展
Docker 容器几乎可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、 个人电脑、服务器等。 这种兼容性可以让用户把一个应用程序从一个平台直接迁移到另外 一个。
更简单的管理
使用 Docker,只需要小小的修改,就可以替代以往大量的更新工作。所有的修改都以 增量的方式被分发和更新,从而实现自动化并且高效的管理。
对比传统虚拟机总结
3、Docker 基本概念
Docker 架构
host --- 主机载体 == docker 安装的地方 继承类比: Class2 extents Class1 -------- Object o = new Class2 ----------------此时,o 对象的结构中,有 Class1 的成员结构 image2 extents image1 ------------Container c = new image2 -------------此时,c 容器中,有 image1 的文件
Docker 镜像
Docker 镜像就是一个只读的模板。
例如:一个镜像可以包含一个完整的 ubuntu 操作系统环境,里面仅安装了 Apache 或 用户需要的其它应用程序。
镜像可以用来创建 Docker 容器。
Docker 提供了一个很简单的机制来创建镜像或者更新现有的镜像,用户甚至可以直接 从其他人那里下载一个已经做好的镜像来直接使用。
Docker 容器
Docker 利用容器来运行应用。
容器是从镜像创建的运行实例。它可以被启动、开始、停止、删除。每个容器都是相互 隔离的、保证安全的平台。
可以把容器看做是一个简易版的 Linux 环境(包括 root 用户权限、进程空间、用户空 间和网络空间等)和运行在其中的应用程序。
Docker 仓库
仓库是集中存放镜像文件的场所。有时候会把仓库和仓库注册服务器(Registry)混为 一谈,并不严格区分。实际上,仓库注册服务器上往往存放着多个仓库,每个仓库中又包含 了多个镜像,每个镜像有不同的标签(tag)。
仓库分为公开仓库(Public)和私有仓库(Private)两种形式。
最大的公开仓库是 Docker Hub,存放了数量庞大的镜像供用户下载。
当然,用户也可以在本地网络内创建一个私有仓库。
当用户创建了自己的镜像之后就可以使用 push 命令将它上传到公有或者私有仓库,这 样下次在另外一台机器上使用这个镜像时候,只需要从仓库上 pull 下来就可以了
容器、镜像的运行关系
4、安装Docker
打开Linux的终端,卸载旧版本
sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine
安装
安装所需资源包
sudo yum install -y yum-utils
设置docker下载地址
sudo yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo
安装docker sudo yum install docker-ce docker-ce-cli containerd.io
查看安装docker列表 yum list docker-ce --showduplicates | sort -r
启动docker sudo systemctl start docker
此时可以使用docker的基本命令了,查看docker的版本 docker -v
查看本地主机上已有镜像的基本信息 docker images
配置docker阿里云镜像加速
在Linux中执行以下命令
-
创建目录
sudo mkdir -p /etc/docker -
配置镜像加速器地址
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://rrnv06ig.mirror.aliyuncs.com"]
}
EOF
-
启动docker后台线程
sudo systemctl daemon-reload -
重启docker服务
sudo systemctl restart docker
Docker 初体验
进入 docker 世界,先来个hello world: docker run hello-world
5、Docker 基本操作
容器操作
docker [run|start|stop|restart|kill|rm|pause|unpause]
- run/create[镜像名]: 创建一个新的容器并运行一个命令
- start/stop/restart[容器名]:启动/停止/重启一个容器
- kill [容器名]: 直接杀掉容器,不给进程响应时间
- rm[容器名]:删除已经停止的容器
- pause/unpause[容器名]:暂停/恢复容器中的进程
docker [ps|inspect|exec|logs|export|import]
- ps:查看容器列表(默认查看正在运行的容器,-a 查看所有容器)
- inspect[容器名]:查看容器配置元数据
- exec -it [容器名] /bin/bash:进入容器环境中交互操作
- logs --since="2019-02-01" -f --tail=10 [容器名]:查看容器日志
- cp path1 [容器名]:path 容器与主机之间的数据拷贝
- export -o test.tar [容器名] / docker export [容器名]>test.tar : 文件系统作为一个 tar 归档文件
- import test.tar [镜像名:版本号]:导入归档文件,成为一个镜像
镜像操作
docker images|rmi|tag|build|history|save|load]
- images:列出本地镜像列表
- rmi [镜像名:版本]:删除镜像
- tag [镜像名:版本] [仓库]/[镜像名:版本]:标记本地镜像,将其归入某一仓库
- build -t [镜像名:版本] [path]:Dockerfile 创建镜像
- history [镜像名:版本]: 查看指定镜像的创建历史
- save -o xxx.tar [镜像名:版本] / save [镜像名:版本]>xxx.tar : 将镜像保存成 tar 归档文件
- load --input xx.tar / docker load
其他
# docker后台启动mysql标准命令,后续也可以把mysql换为其他服务:
docker run -d --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root mysql:8.0
# docker启动nginx,并挂载宿主机文件到docker中
docker run -d --name nginx -v /docker/nginx:/etc/nginx/conf.d nginx
6、镜像与容器原理及用法探究
例:docker history hello-world
显示镜像 hello-world 分两层,其中一个空层
查看镜像文件
镜像存放在 imagedb 里
一般在 image/overlay2/imagedb/content/sha256 下
打开一个镜像文件查看其内容: cat f09fe80eb0e75e97b04b9dfb065ac3fda37a8fac0161f42fca1e6fe4d0977c80
{ "architecture": "amd64", "config": { "Hostname": "", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"], "Cmd": ["/hello"], "ArgsEscaped": true, "Image": "sha256:a6d1aaad8ca65655449a26146699fe9d61240071f6992975be7e720f1cd42440", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": null },"container": "8e2caa5a514bb6d8b4f2a2553e9067498d261a0fd83a96aeaaf303943dff6ff9", "container_config": { "Hostname": "8e2caa5a514b", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"], "Cmd": ["/bin/sh", "-c", "#(nop) ", "CMD [\"/hello\"]"], "ArgsEscaped": true, "Image": "sha256:a6d1aaad8ca65655449a26146699fe9d61240071f6992975be7e720f1cd42440", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": {} },"created": "2019-01-01T01:29:27.650294696Z", "docker_version": "18.06.1-ce", "history": [{ "created": "2019-01-01T01:29:27.416803627Z", "created_by": "/bin/sh -c #(nop) COPY file:f77490f70ce51da25bd21bfc30cb5e1a24b2b65eb37d4af0c327ddc24f0986a6 in / " }, { "created": "2019-01-01T01:29:27.650294696Z", "created_by": "/bin/sh -c #(nop) CMD [\"/hello\"]", "empty_layer": true }], "os": "linux", "rootfs": { "type": "layers", "diff_ids": ["sha256:af0b15c8625bb1938f1d7b17081031f649fd14e6b233688eea3c5483994a66a3"] } }
----其中,history 数组内,标识了镜像的历史记录(与 history 命令内容对应) ----rootfs 的 diff_ids 中,对应了依赖使用中镜像层文件(history 命令中 size 大于 0 的层)
容器创建详解
交互式创建容器并进入:
docker run -it --name centos centos /bin/bash(前台进程)
exit 退出也关闭容器;Ctrl+P+Q退出不关闭容器
后台启动容器
docker run -d --name nginx nginx
进入已运行的容器
docker exec -it nginx /bin/bash
查看容器的元数据
docker inspect nginx
绑定容器端口到主机
docker run -d -p 8080:80 --name nginx nginx:latest
挂载主机文件目录到容器内
docker run -dit -v /root/peter_dir/:/pdir --name cent centos
复制主机文件到容器内
docker cp anaconda-ks.cfg cent:/var
7、镜像的创建
由容器提交镜像
docker 镜像与容器,使用联合文件技术管理文件。
镜像与容器由一层层的 layer 文件组装而成,如下图
- 当用镜像模板创建容器时,是直接在镜像的文件层级上,加一层容器读写层
- 反过来,如果想要创建一个新的镜像,直接把容器对应的所有文件层,转为只读层 即可。commit 命令可达到这个效果,如:
docker commit -a "peter" -m "create image" 605c77e624dd boot:peter
示例 1.创建一个 tomcat 容器
向其中加入一个 war 包:
外部访问是正常的:
2.要复用这个容器,使用 commit
然后浏览器再进行访问8081端口,访问依然是正常的
dockerfile 方式创建容器
虽然使用容器,可以转换成镜像,但不是常规手段。 一情况下,我们使用 dockerfile 方式。
最简单的 dockerfile
创建镜像
使用此镜像运行一个容器
dockerfile 基本要素
dockerfile 指令
FROM
FROM {base 镜像} 必须放在 DOckerfile 的第一行,表示从哪个 baseimage 开始构建
MAINTAINER
可选的,用来标识 image 作者的地方
RUN
RUN 都是启动一个容器、执行命令、然后提交存储层文件变更。
第一层 RUN command1 的执行仅仅是当前进程,一个内存上的变化而已,其结果不会造成任何文件。
而到第二层的时候,启动的是一个全新的容器,跟第一层的容器更完全没关系,自然不可能继承前一层构建过程中的内存变化。
而如果需要将两条命令或者多条命令联合起来执行需要加上&&。
如:cd /usr/local/src && wget xxxxxxx
CMD
CMD 的作用是作为执行 container 时候的默认行为(容器默认的启动命令)
当运行 container 的时候声明了 command,则不再用 image 中的 CMD 默认所定义的命令
一个 Dockerfile 中只能有一个有效的 CMD,当定义多个 CMD 的时候,只有最后一个才会起作用
EXPOSE
EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
entrypoint
entrypoint 的作用是,把整个 container 变成可执行的文件,且不能够通过替换 CMD 的方法来改变创建 container 的方式。但是可以通过参数传递的方法影响到 container 内部
每个 Dockerfile 只能够包含一个 entrypoint,多个 entrypoint 只有最后一个有效
当定义了 entrypoint 以后,CMD 只能够作为参数进行传递
ADD & COPY
把 host 上的文件或者目录复制到 image 中(能够进行自动解压压缩包)
ENV
用来设置环境变量,后续的 RUN 可以使用它所创建的环境变量
WORKDIR
用来指定当前工作目录(或者称为当前目录)
USER
运行 RUN 指令的用户
VOLUME
用来创建一个在 image 之外的 mount point
Docker 容器的主业
docker 理念里,容器启动时,应当为它指定主业是什么,如 nginx 容器主业就是 nginx 代理服务,tomcat 容器就是 web 服务等等
- 容器创建时,必须指定主业任务,如不指定,则容器无事可干立即退出。
- 在 dockerfile 打包镜像时,可以使用 cmd 命令来指定一个默认的主业,如下:
- 既然镜像里是默认主业,即意味着创建容器时,可以覆盖此默认命令,如下:
推荐的 ENTRYPOINT 方式
- 镜像本身应该有稳定的主业,应当指定后即不能更改用途,于是引入 ENTRYPOINT
- 使用 ENTRYPOINT 字义即容器入口,它不能被 run 中 cmd 覆盖,如下例:
执行:docker build -t nginxx:v3
以后使用 nginxx:v3 这个镜像时,只能做 nginx 服务来使用啦
比如拿Tomcat做例子:
创建
起新容器,查看容器日志
可以看到命令已经更改如果此时,再用 echo 命令去覆盖容器,发现不能成功
echo 命令被当作参数付给 entrypoint 指令
手动打包 springboot 镜像
我们需要对业务项目打包发布,一样需要制作成为业务镜像,供运维使用,下面讲述 springboot 的制作过程:
- 将 springboot 打好的 jar 包上传
- 在同级目录下,创建 Dockerfile 文件,内容如
- dockerfile 打包业务镜像
- 启动镜像,即得到业务运行
docker run -d -p 8090:8090 --name member member:v1
然后可以执行相应请求去验证
maven 源码打包用法
更多的情况,我们是直接在运维环境里,上传源码,直接 maven 打包 jar,然后再进一步打包成镜像,与手动打包过程类似
- 上传原码到 docker 环境中(一般是 git/svn 直接拉取源码)
- maven 打包
mvn clean package
生成的 jar 在同级 target 目录
- 执行 docker 命令生成镜像
dockerfile 文件内容
命令创建镜像
maven 插件打包
前面打 springboot 包的方式,需要手动上传项目 jar 或者源码到服务器(违和感很强),这对于开发人员日常发布开发环境项目,极为不便
下面,演示一个 maven 插件:docker-maven-plugin 用法,来打通环境。
项目环境配置 maven 插件
在我们的工程 pom 中加入 docker-maven-plugin 插件的配置,如下:
现在,我们可以使用 mvn 命令,直接编译项目,打包镜像
mvn clean package docker:build
至此,我们的服务器环境,已经可以直接运行 docker run 镜像得到容器服务了
8、仓库使用
1. Docker 官方仓库
注册
官方仓库地址 自由注册,邮件激活即可使用
命令使用
Docker pull/search/login/push/tag
- tag [镜像名:版本] [仓库]/[镜像名:版本]:标记本地镜像,将其归入某一仓库
- Push [仓库]/[镜像名:版本]: 推送镜像到仓库 --需要登陆
- Search [镜像名]:在仓库中查询镜像 – 无法查询到 tag 版本
- Pull [镜像名:版本]: 下载镜像到本地
- Login:登陆仓库
1、命令登陆 dockerhub
2、再使用 tag 命令标记一个镜像,指定自己的仓库
3、使用 push 命令推送此镜像到仓库里
4、打开查询自己仓库的镜像
2. 私有仓库
搭建
下载 registry 镜像:docker pull registry
启动
docker run -d --name reg -p 5000:5000 registry
然后可以通过 restful 接口查看仓库中的镜像(当前仓库是空的)
配置 http 传输
举例说明,使用的127.0.0.1,实际应该换为自己的服务器IP
私服默认只能使用 https,需要配置开放 http
配置完毕重启下 docker 服务
systemctl daemon-reloadsystemctl restart docker
私服仓库推送镜像
docker tag hello-world 127.0.0.1:5000/hello-world
docker push 127.0.0.1:5000/hello-world
查询镜像:curl http://127.0.0.1:5000/v2/_catalog
查询 hello 版本: curl http://127.0.0.1:5000/v2/hello/tags/list