docker简介
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。
Docker 架构
Docker 使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器。Docker 容器通过 Docker 镜像来创建。容器与镜像的关系类似于面向对象编程中的对象与类。
Docker采用 C/S架构 Docker daemon 作为服务端接受来自客户的请求,并处理这些请求(创建、运行、分发容器)。 客户端和服务端既可以运行在一个机器上,也可通过 socket 或者RESTful API 来进行通信。
Docker daemon 一般在宿主主机后台运行,等待接收来自客户端的消息。 Docker 客户端则为用户提供一系列可执行命令,用户用这些命令实现跟 Docker daemon 交互。
docker的应用场景
Automating the packaging and deployment of applications(使应用的打包与部署自动化)
Creation of lightweight, private PAAS environments(创建轻量、私密的PAAS环境)
Automated testing and continuous integration/deployment(实现自动化测试和持续的集成/部署)
Deploying and scaling web apps, databases and backend services(部署与扩展webapp、数据库和后台服务
由于其基于LXC的轻量级虚拟化的特点,docker相比KVM之类最明显的特点就是启动快,资源占用小。因此对于构建隔离的标准化的运行环境,轻量级的PaaS(如dokku), 构建自动化测试和持续集成环境,以及一切可以横向扩展的应用(尤其是需要快速启停来应对峰谷的web应用)。
安装docker
实验环境:rhel7.3
主机名:server1
ip:172.25.26.1
docker软件包下载
[root@server1 docker]# ls
container-selinux-2.21-1.el7.noarch.rpm
docker-ce-18.06.1.ce-3.el7.x86_64.rpm
libsemanage-2.5-8.el7.x86_64.rpm
libsemanage-python-2.5-8.el7.x86_64.rpm
pigz-2.3.4-1.el7.x86_64.rpm
policycoreutils-2.5-17.1.el7.x86_64.rpm
policycoreutils-python-2.5-17.1.el7.x86_64.rpm
[root@server1 docker]# yum install -y *
下载需要的软件包,使用yum安装。
[root@server1 docker]# systemctl start docker.service
[root@server1 docker]# docker info
开启docker,查看状态。
docker的基本应用
导入2048游戏
首先需要获取2048游戏的镜像,之后导入。
[root@server1 images]# docker load -i game2048.tar
011b303988d2: Loading layer 5.05MB/5.05MB
36e9226e74f8: Loading layer 51.46MB/51.46MB
192e9fad2abc: Loading layer 3.584kB/3.584kB
6d7504772167: Loading layer 4.608kB/4.608kB
88fca8ae768a: Loading layer 629.8kB/629.8kB
导入镜像
[root@server1 images]# docker run -d -p 80:80 --name v1 game2048
151ba499cfa839828a5be9c690051e29ee6ed1331127dac7ce6c4fac8b71c10e
创建容器,-p指定映射端口,容器名称为v1。
在浏览器直接输入ip就可以访问了。
导入ubuntu
[root@server1 images]# docker load -i ubuntu.tar
56abdd66ba31: Loading layer 196.8MB/196.8MB
9468150a390c: Loading layer 208.9kB/208.9kB
11083b444c90: Loading layer 4.608kB/4.608kB
5f70bf18a086: Loading layer 1.024kB/1.024kB
导入镜像。
[root@server1 images]# docker run -it --name v2 ubuntu
创建容器。
可以看到,容器和真是主机使用的是一个内核,进入容器后可以使用ctrl+pq把容器打入后台。
[root@server1 images]# docker attach v2
再次连接,需要退出时使用exit退出。
此时,在真实主机上也可以查看容器的网卡信息。
[root@server1 images]# iptables -t nat -nL
此时物理机给容器做了一个dnat。
[root@server1 images]# docker ps
使用docker ps可以查看正在运行的容器,加上-a选项即可以查看全部容器。
使用tab键可以查看到docker的更多用法。
[root@server1 images]# docker stop v1
[root@server1 images]# docker start v1
开启和关闭容器。
[root@server1 images]# docker rm -f v2
强制删除容器。
[root@server1 images]# docker history ubuntu
查看镜像构造过程。
这里在v2容器中创建了一些文件,这些镜像是不会保存到原始的镜像上的,要想保存文件必须创建新的镜像。
[root@server1 images]# docker commit -m "add file" v2 ubuntu:v1
创建提交新的镜像,-m设定标签,设置版本为v1,上面的TAG下面就显示的是版本。
查看镜像的构建,我们发现v1版本的ubuntu镜像只是比原来的版本多了一层,也就是前面设置的标签“add file”部分,每次保存的会在原来的基础上加一层,层数不能多于127层。
创建容器后查看,文件保存下来了,要注意的是,使用这种方法创建的镜像我们并不知道内部做了什么改动。
[root@server1 images]# docker rmi ubuntu:v1
删除镜像,删除镜像时要确保没有容器使用它。
dockerfile的使用。
使用dockerfile的方式创建新的镜像可以
指令 | 作用 |
---|---|
FROM | 指定base镜像,如果本地不存在会从远程仓库下载。 |
MAINTAINER | 设置镜像的作者,比如用户邮箱等 |
COPY | 把文件从build context复制到镜像,支持两种形式:COPY src dest 和 COPY [“src”, “dest”] |
ADD | 用法与COPY类似,不同的是src可以是归档压缩文件,文件会被自动解压到dest,也可以自动下载URL并拷贝到镜像 |
ENV | 设置环境变量,变量可以被后续的指令使用 |
EXPOSE | 如果容器中运行应用服务,可以把服务端口暴露出去 |
VOLUME | 申明数据卷,通常指定的是应用的数据挂在点 |
WORKDIR | 为RUN、CMD、ENTRYPOINT、ADD和COPY指令设置镜像中的当前工作目录,如果目录不存在会自动创建 |
RUN | 在容器中运行命令并创建新的镜像层,常用于安装软件包 |
CMD 与 ENTRYPOINT | 这两个指令都是用于设置容器启动后执行的命令,但CMD会被docker run后面的命令行覆盖,而ENTRYPOINT不会被忽略,一定会被执行。 |
在rhel7的容器内搭建httpd服务。
[root@server1 images]# docker load -i rhel7.tar
e1f5733f050b: Loading layer 147.1MB/147.1MB
导入rhel7镜像
创建目录,编写文件。
[root@server1 docker]# vim dvd.repo
[dvd]
name=rhel7
baseurl=http://172.25.26.250/rhel7.3
gpgcheck=0
编辑yum文件
[root@server1 docker]# vim Dockerfile
FROM rhel7
COPY dvd.repo /etc/yum.repos.d/dvd.repo
RUN rpmdb --rebuilddb && yum install -y httpd
CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"]
在设置在容器内配置yum源并安装和开启httpd。
[root@server1 docker]# docker build -t rhel7:v1 .
生成镜像。
[root@server1 docker]# docker run -d --name apache -p 80:80 rhel7:v1
生成容器,此时,这个容器之内已经配置好httpd服务了。
在浏览器可以访问到测试页,也可以编写好测试页用COPY放到发布目录下。
创建web目录,在里面编写测试页
[root@server1 web]# docker run -d --name apache -p 80:80 -v /tmp/docker/web/:/var/www/html rhel7:v1
创建容器指定80端口映射,用-v选项将目录挂载在到容器的发布目录下。
访问测试页。
[root@server1 web]# vim index.html
修改测试页内容。
用浏览器查看内容同步改变。
使用busybox做测试
[root@server1 images]# docker load -i busybox.tar
导入镜像。
[root@server1 docker]# vim Dockerfile
FROM busybox
ENV name world ##设置环境变量
ENTRYPOINT echo "hello, $name"
[root@server1 docker]# docker build -t busybox:v1 .
生成镜像。
[root@server1 docker]# docker run --rm busybox:v1
hello, world
生成容器,由于这个容器只输出一个结果,没有实际作用,所以在输出结果后就使用删除了。
这里的world就是环境变量。
[root@server1 docker]# vim Dockerfile
FROM busybox
ENTRYPOINT ["/bin/echo", "hello"]
CMD ["world"]
[root@server1 docker]# docker build -t busybox:v2 .
再生成一个镜像。
这里就可以看出CMD和ENTRYPOINT的区别,CMD会被docker run后面的命令行覆盖,而ENTRYPOINT不会被忽略,一定会被执行。
镜像的优化
首先,再一个容器内搭建nginx服务。
[root@server1 docker]# vim Dockerfile
FROM rhel7
COPY dvd.repo /etc/yum.repos.d/dvd.repo
ADD nginx-1.15.8.tar.gz /mnt
WORKDIR /mnt/nginx-1.15.8
RUN rpmdb --rebuilddb && yum install -y gcc make zlib-devel pcre-devel
RUN sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc
RUN ./configure --prefix=/usr/local/nginx
RUN make && make install
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]
编辑文件。
[root@server1 docker]# docker build -t rhel7:v1 .
使用rhel7镜像搭建一个新镜像,在镜像内搭建好服务。
[root@server1 docker]# docker run -d --name nginx -p 80:80 rhel7:v1
创建容器并打开,端口映射设置为80.
打开浏览器访问,服务搭建成功。
[root@server1 docker]# vim Dockerfile
FROM rhel7
COPY dvd.repo /etc/yum.repos.d/dvd.repo
ADD nginx-1.15.8.tar.gz /mnt
WORKDIR /mnt/nginx-1.15.8
RUN rpmdb --rebuilddb && yum install -y gcc make zlib-devel pcre-devel
RUN sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc
RUN ./configure --prefix=/usr/local/nginx
RUN make && make install
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]
设置80端口暴露,申明数据挂载点。
[root@server1 docker]# docker build -t rhel7:v2 .
第二次创建镜像时使用的是缓存,没有重复上面的操作。
创建容器后,我们看到数据目录下出现了index.html文件,编辑这个文件就可以修改发布页内容。
[root@server1 _data]# docker inspect nginx
数据挂载目录。
查看镜像,我们可以看到大小高达276M,所以要对镜像进行优化,毕竟一个服务不会站这么多的空间。
[root@server1 docker]# vim Dockerfile
FROM rhel7 as build
COPY dvd.repo /etc/yum.repos.d/dvd.repo
ADD nginx-1.15.8.tar.gz /mnt
WORKDIR /mnt/nginx-1.15.8
RUN rpmdb --rebuilddb && yum install -y gcc make zlib-devel pcre-devel && sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && ./configure --prefix=/usr/local/nginx && make && make install && rm -rf /mnt/nginx-1.15.8*
FROM rhel7
COPY --from=build /usr/local/nginx /usr/local/nginx
EXPOSE 80
VOLUME ["/usr/local/nginx/html"]
CMD ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;"]
编辑dockerfile,尽量减少镜像的层数,删除中间产物。
[root@server1 docker]# docker build -t rhel7:v3 .
生成v3版本的镜像。
查看之后发现v3版本的镜像已经减少到了141M,不过尽管是这样还是很大,因为底层的bash占的空间太大了,bash环境内有很多东西都是搭建nginx服务用不到的,这样就导致资源的浪费了。
所以要进一步缩减bash环境。
[root@server1 images]# docker load -i distroless.tar
668afdbd4462: Loading layer 18.39MB/18.39MB
Loaded image: gcr.io/distroless/base:latest
导入distroless.tar。
[root@server1 docker]# vim Dockerfile
FROM nginx as base
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
ARG TIME_ZONE
RUN mkdir -p /opt/var/cache/nginx && \
cp -a --parents /usr/lib/nginx /opt && \
cp -a --parents /usr/share/nginx /opt && \
cp -a --parents /var/log/nginx /opt && \
cp -aL --parents /var/run /opt && \
cp -a --parents /etc/nginx /opt && \
cp -a --parents /etc/passwd /opt && \
cp -a --parents /etc/group /opt && \
cp -a --parents /usr/sbin/nginx /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libpcre.so.* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libz.so.* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libc.so.* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libdl.so.* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libpthread.so.* /opt && \
cp -a --parents /lib/x86_64-linux-gnu/libcrypt.so.* /opt && \
cp -a --parents /usr/lib/x86_64-linux-gnu/libssl.so.* /opt && \
cp -a --parents /usr/lib/x86_64-linux-gnu/libcrypto.so.* /opt && \
cp /usr/share/zoneinfo/${TIME_ZONE:-ROC} /opt/etc/localtime
FROM gcr.io/distroless/base
COPY --from=base /opt /
EXPOSE 80
ENTRYPOINT ["nginx", "-g", "daemon off;"]
[root@server1 docker]# docker build -t rhel7:v5 .
查看v5版本,只有23.2M。