什么是Docker
Docker是一种新兴的虚拟化方式,基于Go语言实现的开源项目,诞生于2013年初。
Docker底层基于成熟的Linux Container(LXC)技术实现,自Docker 0.9版本起,引入libcontainer, 打造更通用的底层容器虚拟化库,依赖的底层核心技术主要包括了操作系统命名空间(namespace)、控制组(Control Groups)、联合文件系统(Union File Systems)和 虚拟网络.
为什么使用Docker
- 更快速的交付和部署
- 更高效的资源利用
- 更轻松的迁移和扩展、维护
基础概念
Docker 主要包括三个基本概念:
- 镜像:类似于虚拟机镜像,可以理解成Docker引擎的只读模板,是创建Docker容器的基础
- 容器:类似于一个轻量级的沙箱,Docker利用容器来运行和隔离应用,容器依赖镜像,一个镜像可以运行多个容器
- 仓库:存放镜像文件的场所,比如Docker hub
Docker常用命令
docker命令
docker --help //该命令可以查看docker详细命令
镜像搜索
docker search java
拉取镜像
docker pull java:8 //docker pull REPOSITORY[:TAG],不填版本号默认拉取最新版
镜像列表
docker images //列出镜像列表
删除镜像
docker rmi java:8 //删除镜像,-f 参数代表强制删除
注意:删除镜像应该先删除镜像关联的容器,再删除镜像
创建和运行容器
docker run --name web1 -d -p 8080:80 nginx:v1 //命令详细 docker run --help
常用参数说明:
-t 选项让Docker分配一个伪终端(pseudo-tty) 并绑定到容器的标准输入上
-i 则让容器的标准输入保持打开
-d 后台运行
-p 映射端口 本地端口:容器端口
-P 随机映射端口
--name 指定容器名称
--rm 退出后自动删除
-v 挂载数据卷
容器启停
docker start web1 // 启动 docker start 容器id/容器name
docker stop web1 // 停止 docker stop 容器id/容器name
列出容器
docker ps //列出正在运行的容器
docker ps -a //列出所有的容器
进入容器
docker exec -it web1 /bin/bash
删除容器
docker rm web1
docker rm $(docker ps -aq) //删除所有容器
docker rm $(docker ps -a|grep demo|awk '{print $1}') //按关键字demo过滤删除容器
获取容器元数据
docker inspect web1
查看容器日志
docker logs web1
基于容器创建镜像
docker commit web1 test/web1:v1 //命令详细 docker commit --help
上传镜像
docker tag web1:v1 user/web1:v1 //镜像打标签(实际创建一个新的镜像)
docker push user/web1:v1 //上传镜像
Dockerfile创建镜像
具体参见官网:docs.docker.com/engine/refe…
案例
a. 创建目录
mkdir springbootdemo
cd springbootdemo
vi Dockerfile
b. 创建dockerfile文件:
FROM java:8
MAINTAINER 作者名 邮箱地址
COPY demo1.jar /demo.jar
RUN java -version
EXPOSE 8080
ENV active ''
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/demo.jar","--spring.profiles.active=${active}"]
c. 上传jar包到springbootdemo 目录下
d. 构建镜像
docker build -t demo:v1 . //springbootdemo目录下构建镜像
e. 运行容器
docker run -d -P --name=demo1 -e active="test" demo:v1 //传递参数active="test"
指令说明
FROM
格式为 FROM <image> 或者 FROM <image>:<tag>
必须为第一条指令
MAINTAINER
指定维护者 信息
RUN
格式:RUN <command> 或者 RUN ["executable","param1","param2"]
该命令可以多条,创建镜像时执行的命令,当命令较长时可以使用** \ ** 来换行
CMD
CMD ["executable","param1","param2"]
CMD commond param1 param2
CMD ["param1","param2"] //提供给ENTRYPOINT的默认参数
指定容器启动时执行的命令,每个docker只能有一条CMD命令,如果多条的话,只会执行最后一条,如果用户启动容器指定了运行的命令,则会覆盖CMD命令
EXPOSE
格式 EXPOSE <port> [<port>...]
指定容器暴露的端口号
ENV
格式 ENV <key> <value>
指定环境变量,多个变量多条ENV指令,可以被后续的指定使用${key}
ADD/COPY
格式: ADD <src> <dest> ; COPY <src> <dest>
该命令复制指定src 到容器中dest,其中 src 是dockerfile坐在目录的一个相对路径(文件或者目录),目标路径不存在时会自动创建,两个区别在于,ADD 的src 支持URL,ADD src 是 tar文件会自动解压为目录,推荐使用COPY指令
ENTRYPOINT
ENTRYPOINT ["executable","param1","param2"]
ENTRYPOINT commond param1 param2 //shell中执行
配置容器启动后执行的命令,并且不可被docker run 提供的参数覆盖,只能有一个ENTRYPOINT,当多个时,只有最后一个生效
VOLUME
格式为VOLUME ["/data"]
创建一个挂载点,/data 代表容器中的目录,用于存放需要保持的数据
USER
格式:USER <user>[:<group>] 或者 USER <UID>[:<GID>]
WORKDIR
格式1:WORKDIR /path/to/workdir
格式2:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd //结果/a/b/c
CMD 与 ENTRYPOINT 区别
1.如果没有定义 ENTRYPOINT, CMD 将作为它的 ENTRYPOINT
2.定义了 ENTRYPOINT 的话,CMD 只为 ENTRYPOINT 提供参数
3.CMD 可由 docker run 后的命令覆盖,同时覆盖参数
4.定义了 ENTRYPOINT 的话,docker run
后的命令全是参数,追加到ENTRYPOINT 参数后面
举四个例子进行说明
一, 未定义 ENTRYPOINT, 定义了 CMD
#ENTRYPOINT []
CMD ["echo", "hello"]
实际入口是它们拼接后还是 CMD 本身,["echo", "hello"]
二, 定义了 ENTRYPOINT 和 CMD
ENTRYPOINT ["echo", "hello"]
CMD ["echo", "world"]
实际入口是它们拼接起来,形成 ["echo", "hello", "echo", "world"], 执行 docker run test 显示为 hello echo world
三, 定义了 ENTRYPOINT, CMD 由 docker run 提供
ENTRYPOINT ["echo", "hello"]
执行命令 docker run
rm -rf /, 实际入口是由 ["echo", "hello"] 与 ["rm", "-rf", "/"] 拼接而成的 ["echo", "hello", "rm", "-rf", "/"], 输出为 hello rm -rf /。看到 rm -rf / 也不用担心,用 ENTRYPOINT 就是让人放心
注:ENTRYPOINT 同样可以被覆盖,如 docker run --entrypoint ls test -l /,将会执行 ls -l / 命令。
四, 如果 ENTRYPOINT 用 shell 格式定义的
ENTRYPOINT java -jar /app.jar
CMD ["hello", "world"]
通过 docker inspect 命令看到镜像中实际的 ENTRYPOINT 是 ENTRYPOINT ["/bin/sh", "-c", "java -jar /app.jar"] 所以与 CMD 连接起来的入口就是 ["/bin/sh", "-c", "java -jar /app.jar", "hello", "world"], "bin/sh" 直接忽略掉后面的 "hello" 与 "world",这就是为什么 shell 命令方式无法获取参数。
有了以上几点概念,以及四个实例作为感观认识后,想要怎么往容器传递参数应该很容易确定了。
-
未定义 ENTRYPOINT
没有定义 ENTRYPOINT 的镜像想怎么来就怎么来,docker run后面的输入你自己作主。
-
有定义 ENTRYPOINT
定义了 ENTRYPOINT 的镜像,则是 CMD 或 docker run后的输入作为 ENTRYPOINT 中命令的附加参数。再次提醒 shell 格式的 ENTRYPOINT 和 CMD 务必要转换为相应 exec 格式来理解。
-
shell 格式的 ENTRYPOINT
如果是复杂的 shell 命令不容易拆解出一个个参数,而希望用 shell 格式来定义 ENTRYPOINT 的话,也有办法。shell 格式的 ENTRYPOINT 是由 "/bin/sh -c" 启动的,而它是可以解析变量的。另一方面 CMD 或 docker run的输入第一个元素存成了 @, 所以 shell 格式的 ENTRYPOINT 可以这么写
ENTRYPOINT echo hello @ 注:shell 中 @ 为所有参数
这样执行下面 docker 命令将可获得所有的参数输入
$ docker run test world and China hello world and China
如果只是按常规 shell 脚本来对待,想当然的写成 ENTRYPOINT echo hello $@
效果将是
$ docker run test world and China hello and China
第一个参数将被丢失,docker run
后第一个输入通常是一个命令,所以是 0 $@ 要同时写上。
容器数据管理
数据卷
docker run -d -P --name demo1 -v /data demo:v1 //创建一个数据卷挂载到容器/data目录
docker run -d -P --name demo1 -v /opt/data:/data demo:v1 //挂载主机的/opt/data目录到容器的/data 目录
数据卷容器
docker run -it -v /dbdata --name dbdata ubuntu //数据卷容器
docker run -it --volumes-from dbdata --name db1 ubuntu
docker run -it --volumes-from dbdata --name db2 ubuntu
--volumes-from 容器name, db1,db2 挂载到同一数据卷,三个容器任务一方在该目录下的写入,其它容器都可见,其实就是操作同一目录文件
docker volume ls //查看所有数据卷,详见:docker volume --help
备份
docker run --volumes-from dbdata -v $(pwd):/backup --name db1 ubuntu tar cvf /backup/backup.tar /dbdata
恢复
docker run --volumes-from dbdata -v $(pwd):/backup --name db2 ubuntu tar xvf /backup/backup.tar
容器与主机文件拷贝
docker cp /opt/test.txt demo1:/opt/test //主机copy to 容器
docker cp demo1:/opt/test /opt //容器copy to 主机
Docker目录结构
默认存储目录 Docker Root Dir:/var/lib/docker,存储镜像和容器的元数据以及运行数据
配置文件:** /usr/lib/systemd/system/docker.service**
ExecStart=/usr/bin/dockerd --graph=/home/docker/lib/docker 通过--graph参数修改默认存储目录
重新加载docker
systemctl daemon-reload
systemctl restart docker
配置目录:** /etc/docker ** daemon.json 配置镜像地址