docker是一个支持程序运行的容器,我们可以把网站、程序、项目以及相关环境配置打包放在docker里,然后在一个新的环境中直接运行。
1 概念
docker首先提供了类似集装箱的功能,会将程序及相关依赖文件打包整理,保证在其他环境下也能运行。而且docker提供了标准化存储,我们并不需要关心其内部存储的实现细节。docker还对程序的命令进行了标准化API封装,使用相同的启动命令就可以运行程序。
docker除了解决运行环境不一致所带来的问题之外,还对容器进行隔离和封装,从而保证容器内的程序不会对其他程序造成影响,容器会分配一定的资源,当资源消耗完时就会停止,而不会占用其他资源。此外,由于docker部署的便捷也为快速扩展和弹性伸缩变得简单,我们只要在需要时一条命令启动,不需要时关闭即可。
镜像Image
docker将相关程序打包成镜像,如下图所示,最下层是系统内核kernel,中间就是我们打包好的镜像(例如操作系统镜像Debian、服务器镜像Apache),最上面是容器应用(例如我们写的网站)
镜像的实现是依靠Linux的联合文件系统技术,可以将不同目录下的文件按层次放在一个目录
仓库Repository是用于存放镜像的中央仓库,镜像制作完成后上传到Repository,使用者就可以从中拉取镜像到本地运行
容器Container
应用运行的环境,运行在不同容器的应用彼此独立互不干扰,同一个镜像可以生成多个容器Contrainer。
在docker的分层文件系统中,下面的kernel和Image都是只读的,只有上层的容器是可写的,用于在系统运行中进行日志的写入或文件的修改。如果需要对Image中的内容进行修改,需要将其拷贝到Contrainer中。
2 安装docker
通过yum-config-manager添加yum源
#yum-util提供yum-config-manager功能
#另外两个是devicemapper驱动依赖的
yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
通过yum安装docker
yum install -y docker-ce
启动并验证
systemctl start docker.service
docker version
Client:
Version: 17.09.0-ce
API version: 1.32
Go version: go1.8.3
Git commit: afdb6d4
Built: Tue Sep 26 22:41:23 2017
OS/Arch: linux/amd64
Server:
Version: 17.09.0-ce
API version: 1.32 (minimum version 1.12)
Go version: go1.8.3
Git commit: afdb6d4
Built: Tue Sep 26 22:42:49 2017
OS/Arch: linux/amd64
Experimental: false
3 使用Docker
常用命令
如下所示为docker相关的一些常用命令
# 从默认docker仓库拉取名为NAME的镜像,版本号version可选,默认拉取latest最新的版本
docker pull NAME[:version]
# 查看本地所有的镜像
docker images
# 运行名为IMAGE的镜像,并生成一个容器,参数-d在代表以守护态后台运行,--name指定容器名称
docker run -d IMAGE --name lau-mysql
# 删除指定镜像
docker rmi [镜像id]
# 将容器保存到本地镜像仓库
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
# 查看机器上正在运行的容器,后加参数-a可以查看所有容器
docker ps
# 启动名为lau-mysql的容器
docker start lau-mysql
# 输出容器的日志信息
docker logs lau-mysql
# 停止容器
docker stop lau-mysql
# 删除指定容器
docker rm [容器id]
在不同机器之间传输镜像有两种方式,第一种是将镜像保存为压缩包进行传输,第二种是将镜像上传到镜像仓库然后在需要时通过docker pull
进行拉取
# 将镜像保存成压缩包abc.tar
docker save -o abc.tar mynginx:v1.0
# 其他机器根据压缩包加载镜像
docker load -i abc.tar
# 上传前需要将镜像标签带上远程仓库名前缀mydocker
docker tag mynginx:v1.0 mydocker/mynginx:v1.0
# 登录到docker hub
docker login
# 推送镜像
docker push mydocker/mynginx:v1.0
# 推送完成后退出登录
docker logout
docker pull执行时client发送请求到docker daemon,daemon访问远程仓库下载镜像到本地。
docker run将执行请求发送到daemon,daemon根据本地镜像创建对应的可执行容器Container
进入容器
进入容器CONTAINER内部执行一个命令COMMAND:docker exec [OPTIONS] CONTAINER COMMAND [ARG…]
例如下面命令是在一个名为lau-mysql的容器中执行bash命令,其中参数-i
代表不关闭输入流,-t
代表启动一个虚拟终端,之后我们就进入了容器内部,其内部就像一个小的Linux虚拟机,操作命令完全相同。在其中通过vim对mysql的配置文件进行修改,由于无法直接修改镜像,我们实际上修改的是配置文件的映射mysqld.cnf
docker exec -it lau-mysql bash
# 升级apt
apt-get update
# 安装vim
apt-get install vim
# 修改配置文件的映射
vim /etc/mysql/mysql.conf.d/mysqld.cnf
文件内容如下,我们在其中设置默认字符集为gb2312
[mysqld]
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
datadir = /var/lib/mysql
#log-error = /var/log/mysql/error.log
# By default we only accept connections from localhost
#bind-address = 127.0.0.1
# Disabling symbolic-links is recommended to prevent assorted security risks
#symbolic-links=0
[mysql]
default-character-set=gb2312
端口映射
docker与外界网络交互有两种方式
- 第一种是Host模式,它会和宿主机使用相同的网络端口命名空间,宿主机会把端口直接分配给镜像
- 第二种是Bridge模式,这也是默认模式。docker会创建虚拟网桥,将主机的端口映射到镜像的端口,避免和主机端口冲突。在启动镜像时可以通过
-p
参数指定端口映射,例如下面将宿主机的8080端口映射到镜像的80
docker run -d -p 8080:80 nginx
文件挂载
上面修改配置文件时需要进入到容器内部再进行修改,这样操作很不便利,可以将容器内部的文件挂载到外部主机上的某个位置,这样我们每次在外部主机上就可以对文件进行修改了。
如下所示在启动容器时通过参数-v
将容器内部的/data/html目录挂载到了主机上的/usr/share/nginx/html位置,并且通过ro
指定该文件为只读,即容器内部无法进行修改,或者指定为rw
读写,这样主机和容器都可以对该文件进行修改i
docker run --name=mynginx \
-v /data/html:/usr/share/nginx/html:ro \
nginx
可用通过docker cp
在主机和容器之间进行文件传输
#把容器5eff66eec7e1指定位置的东西复制出来
docker cp 5eff66eec7e1:/etc/nginx/nginx.conf /data/conf/nginx.conf
#把外面的内容复制到容器里面
docker cp /data/conf/nginx.conf 5eff66eec7e1:/etc/nginx/nginx.conf
4 制作镜像
首先需要创建Dockerfile文件告诉docker如何创建镜像
# 从my-tomcat基础镜像为起点
from my-tomcat
# 标记所有者
MAINTAINER tory supertory@qq.com
# 将war包放到tomcat运行目录下
COPY demo.war /usr/local/tomcat/webapps
# 容器启动时执行命令,开启nginx服务
CMD ["nginx", "-g", "daemon off;"]
# 另一种指定启动命令的方式
ENTRYPOINT [ "curl", "-s", "http://ip.cn" ]
接下来构建镜像,通过-t指定镜像名字和标签,并且指定dockerfile所在文件夹,这里.
表示为当前文件夹.
docker build -t my-app:latest .
需要注意的是容器本身就是一个线程,而不是虚拟机,容器中的应用都应该以前台执行,而不是像虚拟机、物理机里面那样用 upstart/systemd 去启动后台服务,因为容器内没有后台服务的概念。例如执行CMD service nginx start
后容器就立即退出了,进而在容器内去使用 systemctl 命令无法执行。
我们要区分清楚任务执行时前台、后台的概念,以及注意容器和虚拟机的差异,对于容器而言其启动程序就是容器应用进程,容器就是作为主进程而存在的,主进程退出容器就会退出。而使用CMD service nginx start 会被理解为 CMD [ "sh", "-c", "service nginx start"]
,因此主进程实际上是 sh。当命令结束后,sh 也会作为主进程退出,自然就会令容器退出。正确的做法是直接执行 nginx 可执行文件,并且要求以前台形式运行CMD ["nginx", "-g", "daemon off;"]
Dockerfile中的ENTRYPOINT
和CMD
指令都可以设置容器启动时要执行的命令,但用途是有略微不同。例如在执行docker run启动容器时如果带有其他命令参数cmd会被覆盖,而使用ENTRYPOINT指令则不会被覆盖。ENTRYPOINT往往用于设置容器启动后的第一个命令,这对一个容器来说往往是固定的;CMD指令用于设置容器启动的第一个命令的默认参数,这对一个容器来说可以是变化的。