Docker学习笔记
为什么使用Docker
环境方便移植组装,应用可以把包和依赖发布到镜像,这样任何docker即可直接拉下来,构建环境,运行软件。
原理
三个基本概念
- 镜像(Image):Docker 镜像(Image),就相当于是一个 root 文件系统。
- 容器(Container):镜像和容器的关系,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
- 仓库(Repository):仓库可看成一个代码控制中心,用来保存镜像。
容器隔离原理
-
PID Namespace进行进程可见性限制:创建进程时设置参数CLONE_NEWPID参数,可以让这个进程有自己的命名空间和进程根目录,结合操作系统的rootfs和联合文件系统就基本实现了docker的隔离问题。
rootfs中包含了一个操作系统所需要的文件,配置和目录,但并不包含系统内核。 因为在 Linux 中,文件和内核是分开存放的,操作系统只有在开启启动时才会加载指定的内核。这也就意味着,所有的容器都会共享宿主机上操作系统的内核。
联合文件系统用于复用docker的镜像层。镜像层由创建容器时构建,镜像层是只读的,只有最上层的容器层才是可写的。具体过程使用了写时拷贝技术 见下一节。
-
Linux Control Group进行资源的限制:主要的作用就是限制进程组使用的资源上限,包括 CPU,内存,磁盘。
Docker Image分层
Docker镜像是由一系列层来构建的,由Dockerfile控制各层,最顶层是可修改的容器层,下面都是镜像层,最下面是。注意:容器层记录对镜像的修改,所有镜像层都是只读的,不会被容器修改,所以镜像可以被多个容器共享。
Container RW
---------
Image Vim R
Image Emacs R
Image Apache R
Baseimage ubuntu R
---------
Kernel R
对镜像中数据的访问使用了写时拷贝技术:
- 添加文件 在容器中创建文件时,新文件被添加到容器层中。
- 读取文件 在容器中读取某个文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,打开并读入内存。
- 修改文件 在容器中修改已存在的文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后修改之。
- 删除文件 在容器中删除文件时,Docker 也是从上往下依次在镜像层中查找此文件。找到后,会在容器层中记录下此删除操作。
Docker和虚拟机
- 传统 VM: 需要基于 Hypervisor 的硬件虚拟化技术,模拟出 CPU,内存等硬件。然后在其上搭建一套完整的操作系统,自然在性能上会有很大的损失。迁移自然更不用说,传统的 ova 导出后就是一个完整的操作系统。
- Docker:Docker 将 Hypervisor 的位置换成自己的 Docekr Engine。然后运行的容器仅仅是一个特殊的进程,自然性能不会有太大的损失。并且可以应用和其所需要的系统文件打包成镜像,无论在哪读可以正常运行,而且相对于 ova 来说体积也小了更多。(需要内核支持)
命令
镜像相关
镜像搜索与安装
$docker search imagename
$docker pull imagename
镜像删除
需要与之关联的容器已删除
$docker rmi imageID
# 删除所有镜像
$docker rmi $(docker images -aq)
本地镜像列表
$docker images
镜像构建与发布
前提是已经有了Dokerfile。
$docker build .
- -f 指定Dockerfile文件路径
- -t 镜像名字与标签 name:tag 格式
容器相关
创建并启动容器(创建实例)
$docker run imagename
-
dockername:version 版本号,默认latest
-
–name myname容器命名,使用命名可以代替dockerID
-
-P 将容器80端口映射至主机随机端口
-
-p machineport: dockerport 将主机端口映射至容器端口
-
-v machineport:dockerport 将主机目录与容器目录双向绑定,注意主机目录在前,可以在inpect里的Mounts里看到绑定信息,多个绑定多个-v即可
-
-d 后台运行,返回容器ID
# 实例 $docker run -p 80:80 -v /data:/data --name myngnix ngnix:latest -
-it bashpath 与容器内bash进行交互
# 实例 与cntos的bash进行交互 $docker run -it centos /bin/bash -
-e 设置环境变量
# 实例 $docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MY_SQL_ROOT_PASSWORD=123456 --name mysql01 mysql
进入与退出容器
若在run里加入-d后台运行,想要进入使用以下命令
$docker attach dockerID # 退出后停止docker
$docker exec dockerID -it # (命令行)退出后不停止
$docker exit
显示(正在运行)容器
$docker ps
- -a 显示所有容器
- -q 只显示ID,静默模式,常用于遍历所有ID
停止/运行/重启容器
$docker stop dockerID
$docker start dockerID # 与run的区别是一个是创建实例一个是已经创建好,停止后再启动
$docker restart dockerID
删除容器
容器需要是已经停止
$docker rm -f dockerID
$docker rm -f $(docker ps -aq)
查看网络端口映射
$docker port dockerID
查看程序内部标准输出
$docker logs
- -f 跟踪日志输出
- -tail num 显示最近num条日志
查看容器内部运行进程
$docker top
查看容器详细信息
$docker inspect
动态修改环境变量
- 停止容器
- 停止服务
- 修改
/var/lib/docker/containers/dockerID/config.v2.json里的Env列表,注:该json就是docker inspect dockerID显示的那个json - 启动服务
- 启动容器
$docker stop containerID
$service docker stop
$cd /var/lib/docker/containers
# 找到dockerID进入,修改config.v2.json并修改
$service docker start
$docker start containerID
注意,在ubuntu下需要提升cd权限,在su下进行使用sudo -s 进行相关更改后exit退出su权限,注意在su下要万分小心的更改东西。
知识
挂载
挂载可以实现数据持久化,使得删除容器后数据还在。所谓挂载就是把容器内文件目录与宿主机(docker engine)的某个目录(称为“卷”)进行双向绑定。
挂载方式
docker卷的默认工作目录是/var/lib/docker/volumes,如果在指定挂载时没有提供宿主机的绝对路径,那么就会在工作目录下直接创建卷(相对路径)。如果提供了名称,卷就是这个名称;如果没提供就会是一段随机字符。通常使用具名挂载。
那么挂载简单的可以分为三种:使用如下:
-
具体路径挂载:
-v machinepath:dockerpath -
具名挂载:
-v vname:dockerpath注:若dockerpath不存在则在docker内创建 -
匿名挂载
-v dockerpath注:只有一个名字则在这个docker的根目录创建这个目录
具名只提供了一个名称或者说是相对路径、匿名则没有提供路径,所以二者都是直接放在工作目录下,具名只是为随机的卷名重命名而已。而machinepath一般都是/开头的绝对路径,自然也就是指定目录了。
例如:
# 具名挂载例子
$docker run -v mysqlOnMachine:/etc mysql
...
$cd /var/lib/docker/volumes
$ls
... ... mysqlOnMachine# 在卷工作目录下看到了具名挂载的那个卷
权限设置
还可以设置容器对这个卷的权限,readonly ro,readwirte rw。若是ro则容器不能更改此卷只能在宿主机更改。
$docker run -v vname:dockerpath:rw imagename
docker间共享卷
多个docker可以共享一个持久化空间,即是共享一个宿主机空间。使用如下
# 创建一个容器docker2,和已经创建了的docker1共享卷
$docker run --volumes-from docker1 docker2
Dockerfile
Dockerfile是docker的构建文件。是一个命令-参数脚本。
特点:
- 命令+参数,命令全大写字母
- 从上向下逐行执行
- 逐层累积
Dockerfile编写模板
FROM centos # 基础镜像
MAINTAINER name<mail.com> # 维护者信息
ENV MYPATH /user # 环境变量
WORKDIR $MYPATH # 设置工作目录,别人进入该docker时所在目录,不然就是根目录了
RUN yum -y install vim # 在docker build时执行命令
RUN yum -y net-tools
VOLUME [“volumename”] # 相当于-v。匿名挂载,这里的volumename是docker内的目录,若没有,则创建。这个目录和宿主机的某个目录是同步的,宿主机目录在卷工作目录下。
COPY /home /mydockerhome # 添加文件,源 目标。目标不存在会自动创建
EXPOSE 80 # 相当于-p,仅仅是声明,并没有绑定。方便配置而已
CMD ls -a # 在docker run时执行命令,为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。如果Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效
ENTRYPOINT -a # 在docker run时执行命令,类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。如果Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。ENTRYPOINT和CMD配合实现定参和变参。
ONBUILD ls -a # 触发指令,有新的image继承本image时
坑
ubuntu每次都需要加sudo
原因:
docker server没有将当前用户加入su组
解决:
$sudo groupadd docker #添加docker用户组
$sudo gpasswd -a $USER docker #将登陆用户加入到docker用户组中
$newgrp docker #更新用户组
$docker ps #测试docker命令是否可以使用sudo正常使用
创建centos时没有指定bash导致无法交互
解决:
关闭docker,启动docker加入-i选项
$docker stop centosID
$docker start -i centosID
防火墙禁用全部INPORT导致外网无法访问服务
解决:
# 全开,应该依赖VPS的防火墙
$iptables -I INPUT -j ACCEPT
没有指定终端 无法进入容器
解决:
把-it放在dockername前面,放在dockername前面是指令
$docker exec -it myhduhelp /bin/sh