文章目录
Docker 说明
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口,更重要的是容器性能开销极低。
Docker的应用场景:
- Web 应用的自动化打包和发布。
- 自动化测试和持续集成、发布。
- 在服务型环境中部署和调整数据库或其他的后台应用。
- 从头编译或者扩展现有的 OpenShift 或 Cloud Foundry 平台来搭建自己的 PaaS 环境。
Docker 是一个用于开发,交付和运行应用程序的开放平台。Docker 使您能够将应用程序与基础架构分开,从而可以快速交付软件。借助 Docker,您可以与管理应用程序相同的方式来管理基础架构。通过利用 Docker 的方法来快速交付,测试和部署代码,您可以大大减少编写代码和在生产环境中运行代码之间的延迟。
安装
所需要安装的数据包
containerd.io-1.2.5-3.1.el7.x86_64.rpm
container-selinux-2.21-1.el7.noarch.rpm
docker-ce-18.09.6-3.el7.x86_64.rpm
docker-ce-cli-18.09.6-3.el7.x86_64.rpm
安装后设置启动并开机自启,docker的数据文件在 /var/lib/docker 目录里
[root@server1 ~]# docker images #查看系统中存在的镜像
[root@server1 ~]# docker load -i 容器名 #加载容器
这里我在网上下载的一个测试用的容器,是一个2048的游戏
[root@server1 ~]# docker run -d --name game -p 80:80 game2048
#运行容器,-d表示后台运行,--name指定名称,-p因为外部不能直接访问容易内容,需要通过本机的80访问容器的80端口
[root@server1 ~]# docker ps #查看容器的进程
访问通过访问server1的域名可以直接看到docker容器的内容
并且这个游戏的大小只有55M,就非常方便可以随意拷贝使用
[root@server1 ~]# du -sh game2048.tar
55M game2048.tar
使用docker info 查看docker信息时,发现有两个warning
查看到这两条是0,就需要我们去设置为1开启这两个功能
可以自行编写开启设定,加载设定
[root@server1 ~]# vim /etc/sysctl.d/docker.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
[root@server1 ~]# sysctl --system 重新加载参数,我们编写的设定文件也会被加载进去
再去查看docker信息就不会有warning了
docker操作
docker镜像
测试使用Ubuntu Linux的桌面系统,也是可以在网上找到下载用的
[root@server1 ~]# du -sh ubuntu.tar
188M ubuntu.tar #这个docker包就比较大了,有188M
[root@server1 ~]# docker load -i ubuntu.tar #加载到docker里
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
Loaded image: ubuntu:latest
[root@server1 ~]# docker run -it --name ubuntu ubuntu #-it表示交互式运行,开启在前端上
root@64adc2e500ca:/# uname -r
3.10.0-957.el7.x86_64 #并且它的内核版本和宿主机的版本是一样的,因为它是基于宿主机的linux系统去运行的
我们在里面建立文件,建立1、2两个文件
退出系统,再次进入发现操作没有被保存下来
root@64adc2e500ca:/# exit #执行退出ubuntu系统
exit
[root@server1 ~]# docker ps -a #查看所有的docker进程
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
64adc2e500ca ubuntu "/bin/bash" 7 minutes ago Exited (0) 8 seconds ago ubuntu
[root@server1 ~]# docker rm -f ubuntu #删除这个运行过的ubuntu
ubuntu
[root@server1 ~]# docker run -it --name ubuntu ubuntu #再次运行
root@4cc870978275:/# ls
bin dev home lib64 mnt proc run srv tmp var
boot etc lib media opt root sbin sys usr
#在里面可以看到之前的创建文件都没有了,操作没有保存下来
这是因为,我们这里所作的操作都是在镜像层,docker 使用 copy-on-write 机制,镜像层是只读的,不可作更改,就像是使用虚拟机快照,快照可以使用主系统里的所有东西,但是对主系统没有任何的更改操作。
可以通过命令docker history ubuntu:latest,查看镜像的记录
这里面显示的就是镜像创建的所有操作,如果进行改动只能在这个操作之上添加新的操作,再打包成为新的镜像
docker 镜像构建和参数
当在交互式运行的系统里使用exit退出系统时,后台的进程也会退出,那我们作的设定也同样会消失,如果想让镜像后台运行,使用按键【Ctrl】+【p】+【q】同时按就可以将镜像放置后台,使用docker attach name在开启到前台来,这样就不会失去之前的操作。
保存提交更改过镜像内容
这里测试使用一个很小的linux文件系统镜像,我们运行并在里面进行更改操作,然后重新打包镜像发布
[root@server1 ~]# du -sh busybox.tar
1.4M busybox.tar
[root@server1 ~]# docker load -i busybox.tar #加载镜像
8a788232037e: Loading layer 1.37MB/1.37MB
Loaded image: busybox:latest
[root@server1 ~]# docker run -it --name linux busybox #运行镜像
/ # ls #查看镜像里面的内容
bin dev etc home proc root sys tmp usr var
/ # echo "hello world" > /file1 #创建一个文件file1,并添加一些内容
/ # cat file1
hello world
将镜像放置后台,执行命令docker commit vm1 test:v1,提交更改过的内容
/ # read escape sequence
[root@server1 ~]# docker commit linux test:v1
然后对比我们保存的镜像和之前的镜像,就是多了一条操作
dockerfile
这里还存在的一个问题是,我们打包的镜像和人家的相比,我们增加的操作操作内容是看不到的,这种操作就很不安全,别人如果想要使用你的镜像但是看不到你执行的操作,这就是被认为不安全的,所以我们要怎么将我们的操作也添加到打包的镜像里和别人做的一样优秀呢?
dockerfile 关键字RUN
[root@server1 ~]# mkdir docker
[root@server1 ~]# vim docker/dockerfile
FROM busybox #指定以哪个镜像为基础
RUN echo testfile1 > file1 #指定要做的事情
RUN echo testfile2 > file2
[root@server1 docker]# cd docker/
[root@server1 docker]# docker build -t test:v2 . #建立镜像,“.”表示建立当前目录下
[root@server1 docker]# docker history test:v2 #查看镜像的历史,就可以看到详细的构建信息
IMAGE CREATED CREATED BY SIZE COMMENT
ea0e15e80ba8 About a minute ago /bin/sh -c echo testfile2 > file2 10B
44387f41994e About a minute ago /bin/sh -c echo testfile1 > file1 10B
59788edf1f3e 20 months ago /bin/sh -c #(nop) CMD ["sh"] 0B
<missing> 20 months ago /bin/sh -c #(nop) ADD file:63eebd629a5f7558c… 1.15MB
镜像构建过程,镜像构建时可以看到这个过程
[root@server1 docker]# docker build -t test:v2 .
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM busybox
---> 59788edf1f3e
Step 2/3 : RUN echo testfile1 > file1 #执行操作
---> Running in 34be27b948e4 #建立临时容器执行操作
Removing intermediate container 34be27b948e4 #执行完后删除临时容器
---> 44387f41994e
Step 3/3 : RUN echo testfile2 > file2
---> Running in 36d4852e0492
Removing intermediate container 36d4852e0492
---> ea0e15e80ba8 #最后执行完所有操作后保存在这个id的容器里
Successfully built ea0e15e80ba8
Successfully tagged test:v2
测试我们在操作里添加一条新操作,再去构建镜像
[root@server1 docker]# vim dockerfile
FROM busybox
RUN echo testfile1 > file1
RUN echo testfile2 > file2
RUN echo testfile3 > file3
[root@server1 docker]# docker build -t test:v3 .
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM busybox
---> 59788edf1f3e
Step 2/4 : RUN echo testfile1 > file1
---> Using cache #这里就没有再去创建临时容器执行操作,直接使用了之前的容器内容
---> 44387f41994e
Step 3/4 : RUN echo testfile2 > file2
---> Using cache
---> ea0e15e80ba8
Step 4/4 : RUN echo testfile3 > file3
---> Running in a76f460f88a1 #除了这一步创建临时容器进行操作
Removing intermediate container a76f460f88a1
---> e40975cc4e7b
Successfully built e40975cc4e7b
Successfully tagged test:v3
这样操作的是因为我们没有更改dockerfile里之前的操作内容,如果有任何的更改都会出现构建镜像时重新构建,只能新增操作。
dockerfile 关键字COPY
清空我们刚才生成的镜像,然后来进行创建新镜像
[root@server1 docker]# docker rmi test:v2 #删除镜像命令
[root@server1 docker]# docker rmi test:v3
重新编写dockerfile
[root@server1 docker]# vim dockerfile
FROM busybox
COPY testfile /tmp #复制testfile文件到/tmp目录里
[root@server1 docker]# echo "hello world" > testfile
这里我们要自行创建文件testfile,因为它执行是从当前目录下复制过去的,然后建立镜像
[root@server1 docker]# docker build -t test:v1 .
[root@server1 docker]# docker history test:v1
IMAGE CREATED CREATED BY SIZE COMMENT
5712e7ffeeb3 36 seconds ago /bin/sh -c #(nop) COPY file:9144eb84ab54e5c9… 12B
59788edf1f3e 20 months ago /bin/sh -c #(nop) CMD ["sh"] 0B
<missing> 20 months ago /bin/sh -c #(nop) ADD file:63eebd629a5f7558c… 1.15MB
运行我们的镜像,查看文件是否复制在 /tmp里
root@server1 docker]# docker run -it --name vm1 test:v1
/ # ls /tmp
testfile
dockerfile 关键字ADD
[root@server1 docker]# docker rm -f vm1 #删除刚才运行的镜像
[root@server1 docker]# vim dockerfile
FROM busybox
COPY testfile /tmp
ADD nginx-1.18.0.tar.gz /tmp #使用ADD添加nginx服务
这里需要把nginx源码包放在当前docker目录里
[root@server1 docker]# docker build -t test:v2 .
[root@server1 docker]# docker run -it --name vm1 test:v2 #运行镜像
/ # ls /tmp
nginx-1.18.0 testfile #可以看到nginx已经自动解压好了
VOLUME数据卷
[root@server1 docker]# vim dockerfile
FROM busybox
COPY testfile /tmp
ADD nginx-1.18.0.tar.gz /tmp
VOLUME ["/data"] #在容器启动时会自动新建这个目录
[root@server1 docker]# docker build -t test:v3 . #创建镜像
[root@server1 docker]# docker run -it --name vm2 test:v3 #运行
/ # ls
bin data dev etc home proc root sys tmp usr var #data目录已经自动建立出来里
并且使用【Ctrl】+【p】+【q】后台运行,命令:[root@server1 docker]# docker inspect vm2 查看镜像的具体信息
可以看到这个信息,这个路径就是镜像里data目录的挂载点
进入这个路径,我们去创建一个文件,再回到镜像里可以看到也是同步显示的
[root@server1 docker]# cd /var/lib/docker/volumes/558a1e8146835d5adc3952afef15aa3810cf30af005f4785183a89518ef7d125/_data
[root@server1 _data]# ls
[root@server1 _data]# touch test1
[root@server1 _data]# docker attach vm2
/ # ls /data/
test1
这里我们去找的路径名字非常的长,不方便我们使用,所以我们也可以手动指定数据卷的挂载位置
[root@server1 _data]# docker run -it --name vm3 -v /opt/data:/data test:v3 #再重新开启一个v3的镜像,-v参数可以指定数据卷的挂载“挂载路径:数据卷”
/ # touch /data/file1 #在data目录里创建文件file1
/ # #切换到后台
[root@server1 _data]# cd /opt/data #切换到指定挂载的目录
[root@server1 data]# ls
file1 #可以看到镜像里创建的文件
- WORKDIR(为 ADD RUN COPY 等命令设置在镜像中的工作目录,不存在会新建)
- CMD 和 ENTRYPOINT 都是用于设置容器启动后执行的命令,但是 CMD 会被 docker run 后
面的命令行覆盖,而 ENTRYPOINT 一定会被执行 - docker run 后面的参数可以传递给 ENTRYPOINT 指令当作参数。Dockerfile 中只能指定一个
ENTRYPOINT,如果有多个,只有最后一个生效
shell格式的dockerfile
编写新的dockerfile,使用变量
[root@server1 docker]# vim dockerfile
FROM busybox
ENV name world #定义变量name,值为world
ENTRYPOINT echo "hello, $name" #输出一个hello world
[root@server1 docker]# docker build -t test:v4 .
[root@server1 docker]# docker run --rm test:v4 #--rm表示该运行为一次性,执行完后就关闭容器镜像
hello, world
exec 格式的dockerfile
编写dockerfile
[root@server1 docker]# vim dockerfile
FROM busybox
ENV name world
ENTRYPOINT ["/bin/sh","-c", "echo hello $name"]
[root@server1 docker]# docker build -t test:v5 .
[root@server1 docker]# docker run --rm test:v5
hello world
还可以加上CMD,来测试执行
[root@server1 docker]# vim dockerfile
FROM busybox
ENV name world
ENTRYPOINT ["/bin/echo","hello"]
CMD ["world"]
[root@server1 docker]# docker build -t test:v6 .
[root@server1 docker]# docker run --rm test:v6
hello world
[root@server1 docker]# docker run --rm test:v6 redhat #给命令后加上redhat参数
hello redhat #执行后就会覆盖CMD的参数
构建镜像
构建nginx
使用rhel7的基础镜像架构nginx
[root@server1 ~]# du -sh rhel7.tar
141M rhel7.tar
[root@server1 ~]# docker load -i rhel7.tar
e1f5733f050b: Loading layer 147.1MB/147.1MB
[root@server1 docker]# vim dockerfile
FROM rhel7 #使用rhel7镜像
EXPOSE 80 #开放80端口
MAINTAINER he646617320@163.com #技术联系,可以不写
COPY test.repo /etc/yum.repos.d/ #复制yum 配置文件
RUN rpmdb --rebuilddb #在 yum 之前重新构建 rpm 数据库
RUN yum install -y gcc make pcre-devel zlib-devel #安装编译所需要的依赖性
ADD nginx-1.18.0.tar.gz /mnt #添加nginx包
WORKDIR /mnt/nginx-1.18.0 #指定工作目录
RUN ./configure --prefix=/usr/local/nginx #编译
RUN make
RUN make install #安装
ENTRYPOINT ["/usr/local/nginx/sbin/nginx","-g","daemon off;"] #运行容器镜像时启动nginx
[root@server1 docker]# docker build -t test:v1 . #建立镜像
[root@server1 docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test v1 8ed3122f748c 15 seconds ago 323MB #这个镜像的大小是323M
我们建立的这个镜像还是比较打的,所以需要进行优化,减小大小
- 优化dockerfile里的构建过程中的产物
FROM rhel7
EXPOSE 80
MAINTAINER he646617320@163.com
COPY test.repo /etc/yum.repos.d/
RUN rpmdb --rebuilddb
RUN yum install -y gcc make pcre-devel zlib-devel && yum clean all #清理yum缓存
ADD nginx-1.18.0.tar.gz /mnt
WORKDIR /mnt/nginx-1.18.0
RUN ./configure --prefix=/usr/local/nginx
RUN make
RUN make install
RUN rm -fr /mnt/nginx-1.18.0 #删除编译安装过的nginx包
ENTRYPOINT ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
[root@server1 docker]# docker build -t test:v2 . #再次构建镜像
[root@server1 docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test v2 87b886d71698 11 seconds ago 297MB #相比之前变小了
test v1 8ed3122f748c 9 minutes ago 323MB
- 优化减少镜像的层数,合并执行
FROM rhel7
EXPOSE 80
MAINTAINER he646617320@163.com
COPY test.repo /etc/yum.repos.d/
ADD nginx-1.18.0.tar.gz /mnt
WORKDIR /mnt/nginx-1.18.0
RUN rpmdb --rebuilddb && yum install -y gcc make pcre-devel zlib-devel && yum clean all && ./configure --prefix=/usr/local/nginx && make && make install && rm -fr /mnt/nginx-1.18.0
ENTRYPOINT ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
[root@server1 docker]# docker build -t test:v3 #再去构建镜像
[root@server1 docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test v3 48ce85c416a4 12 seconds ago 278MB #再次变小了
- 优化使用多阶段构建镜像
在构建 nginx 镜像时,我们最终需要的只是/var/local/nginx/sbin/nginx 这个二进制文件,其他的过程都是为了获取它所产生的,
所以我们可以在一个镜像中先构建出这个文件,在把它复制到另一个镜像中,这样创建的镜像就很小了
[root@server1 docker]# vim dockerfile
FROM rhel7 as build #使这个镜像作为建立过程镜像
EXPOSE 80
MAINTAINER he646617320@163.com
COPY test.repo /etc/yum.repos.d/
ADD nginx-1.18.0.tar.gz /mnt
WORKDIR /mnt/nginx-1.18.0
RUN sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && rpmdb --rebuilddb && yum install -y gcc make pcre-devel zlib-devel && yum clean all && ./configure --prefix=/usr/local/nginx && make && make install && rm -fr /mnt/nginx-1.18.0
#这里关闭了nginx的debug功能
FROM rhel7
EXPOSE 80
MAINTAINER he646617320@163.com
VOLUME ["/usr/local/nginx/html"] #使用容器时通过宿主机的nginx发布页路径发布
COPY --from=build /usr/local/nginx /usr/local/nginx #从过程镜像上复制nginx目录
ENTRYPOINT ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
[root@server1 docker]# docker build -t test:v4 .
[root@server1 docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test v4 f8d1ceba2362 12 seconds ago 141MB #大小只剩下144M了
- 优化基础镜像
可以看到经过我们之前的优化,最后安装的nginx只有1M
[root@server1 docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test v4 938a0ef49bc2 12 seconds ago 141MB
rhel7 latest 0a3eb3fde7fd 5 years ago 140MB
所以,可以选择更精简的基础镜像减小我们的镜像大小。
这些基础镜像可以通过网上下载,如github网站等
测试运行访问nginx镜像
开启镜像
[root@server1 docker]# docker run -d --name nginx test:v5
[root@server1 distroless]# docker inspect nginx #查看容器的详细信息
在最后的信息里可以看到镜像的ip和网关
并且在开启镜像时,本机也会开启一个docker0的桥接
因为容器的网关是docker0的ip,所以我们可以直接去访问nginx
[root@server1 docker]# curl 172.17.0.2
但是为了让外界可以访问到我们使用docker布置的容器镜像,使用端口映射,将容器的80端口映射到宿主机的80端口
[root@server1 docker]# docker run -d -p 80:80 --name nginx test:v5 #确保宿主机 80端口没有被占用
然后可以看到宿主机的80端口被docker-proxy开启
通过网页就可以访问nginx了