Docker 镜像制作和管理

2.1 Docker 镜像说明

2.1.1 Docker 镜像中有没有内核

从镜像大小上面来说,一个比较小的镜像只有1MB多点或几MB,而内核文件需要几十MB, 因此镜像里面是没有内核的,镜像在被启动为容器后将直接使用宿主机的内核,而镜像本身则只提供相应的rootfs,即系统正常运行所必须的用户空间的文件系统,比如: /dev/,/proc,/bin,/etc等目录,容器当中/boot目录是空的,而/boot当中保存的就是与内核相关的文件和目录。

2.1.2 为什么没有内核

由于容器启动和运行过程中是直接使用了宿主机的内核,不会直接调用物理硬件,所以也不会涉及到硬件驱动,因此也无需容器内拥有自已的内核和驱动。而如果使用虚拟机技术,对应每个虚拟机都有自已独立的内核。

2.1.3 容器中的程序后台运行会导致此容器启动后立即退出

Docker容器如果希望启动后能持续运行,就必须有一个能前台持续运行的进程,如果在容器中启动传统的服务,如:httpd,php-fpm等均为后台进程模式运行,就导致 docker 在前台没有运行的应用,这样的容器启动后会立即退出。所以一般会将服务程序以前台方式运行,对于有一些可能不知道怎么实现前台运行的程序,只需要在你启动的该程序之后添加类似于 tail ,top 这种可以前台运行的程序即可. 比较常用的 方法,如 tail -f /etc/hosts 。

范例:

#httpd
ENTRYPOINT [ "/usr/sbin/apache2" ]  
CMD ["-D", "FOREGROUND"]  
#nginx
ENTRYPOINT [ "/usr/sbin/nginx", "-g", "daemon off;" ]

#用脚本运行容器
cat run_haproxy.sh 
#!/bin/bash
haproxy -f /etc/haproxy/haproxy.cfg
tail -f /etc/hosts
tail -n1 Dockerfile 
CMD ["run_haproxy.sh"]

2.1.4 docker 镜像生命周期

itony-20211229105730292

2.1.5 制作镜像方式

Docker 镜像制作类似于虚拟机的镜像(模版)制作,即按照公司的实际业务需求将需要安装的软件、相关配置等基础环境配置完成,然后将其做成镜像,最后再批量从镜像批量生成容器实例,这样可以极大的简化相同环境的部署工作.

Docker的镜像制作分为手动制作(基于容器)和自动制作(基于DockerFile),企业通常都是基于Dockerfile制作镜像.

docker commit #通过修改现有容器,将之手动构建为镜像
docker build  #通过Dockerfile文件,批量构建为镜像

2.2 将现有容器通过 docker commit 手动构建镜像

2.2.1 基于容器手动制作镜像步骤

docker commit 格式

docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
#选项
-a, --author string   Author (e.g., "John Hannibal Smith <hannibal@ateam.com>")
-c, --change list     Apply Dockerfile instruction to the created image
-m, --message string   Commit message
-p, --pause           Pause container during commit (default true)

docker commit -a "test<root@test.com>" -c 'CMD /bin/httpd -fv -h /data/html' -c "EXPOSE 80" b1 httpd-busybox:v1.0

#说明:
制作镜像和CONTAINER状态无关,停止状态也可以制作镜像
如果没有指定[REPOSITORY[:TAG]],REPOSITORY和TAG都为<none>
提交的时候标记TAG号: 生产当中常用,后期可以根据TAG标记创建不同版本的镜像以及创建不同版本的容器

基于容器手动制作镜像步骤具体如下:

  1. 下载一个系统的官方基础镜像,如: CentOS 或 Ubuntu
  2. 基于基础镜像启动一个容器,并进入到容器
  3. 在容器里面做配置操作
  • 安装基础命令
  • 配置运行环境
  • 安装服务和配置服务
  • 放业务程序代码
  1. 提交为一个新镜像 docker commit
  2. 基于自己的的镜像创建容器并测试访问

2.2.2 实战案例: 基于 busybox 制作 httpd 镜像

[root@ubuntu1804 ~]#docker run -it --name b1 busybox 
/ # ls
bin   dev   etc   home proc root sys   tmp   usr   var
/ # mkdir /data/html -p
/ # echo httpd website in busybox > /data/html/index.html
/ # httpd --help
BusyBox v1.32.0 (2020-06-27 00:20:57 UTC) multi-call binary
Usage: httpd [-ifv[v]] [-c CONFFILE] [-p [IP:]PORT] [-u USER[:GRP]] [-r REALM] 
[-h HOME]
or httpd -d/-e/-m STRING
Listen for incoming HTTP requests
	 -i Inetd mode
	 -f Don't daemonize
	 -v[v] Verbose 显示访问日志
	 -p [IP:]PORT Bind to IP:PORT (default *:80)
	 -u USER[:GRP] Set uid/gid after binding to port
	 -r REALM Authentication Realm for Basic Authentication
	 -h HOME Home directory (default .)
	 -c FILE Configuration file (default {/etc,HOME}/httpd.conf)
	 -m STRING MD5 crypt STRING
	 -e STRING HTML encode STRING
	 -d STRING URL decode STRING
/ # exit

httpd命令选项解释:
    -c<httpd指令> 在读取配置文件前,先执行选项中的指令。
    -C<httpd指令> 在读取配置文件后,再执行选项中的指令。
    -d<服务器根目录> 指定服务器的根目录。
    -D<设定文件参数> 指定要传入配置文件的参数。
    -f<设定文件> 指定配置文件。
    -h 显示帮助。
    -l 显示服务器编译时所包含的模块。
    -L 显示httpd指令的说明。
    -S 显示配置文件中的设定。
    -t 测试配置文件的语法是否正确。
    -v 显示版本信息。
    -V 显示版本信息以及建立环境。
    -X 以单一程序的方式来启动服务器。

#格式1
[root@ubuntu1804 ~]#docker commit -a "test<root@test.com>" -c 'CMD /bin/httpd -fv -h /data/html' -c "EXPOSE 80" b1 httpd-busybox:v1.0

#格式2
[root@ubuntu1804 ~]#docker commit -a "test<root@test1.com>" -c 'CMD ["/bin/httpd", "-f", "-v","-h", "/data/html"]' -c "EXPOSE 80" b1 httpdbusybox:v1.0

[root@ubuntu1804 ~]#docker images
REPOSITORY         TAG                 Itony ID           CREATED             
SIZE
httpd-busybox       v1.0               e7883146c119        6 minutes ago       
1.22MB

[root@ubuntu1804 ~]#docker run -d -P --name httpd01 httpd-busybox:v1.0 
ce95174c6385392b9699d12d1a86d7f81bc4dde1400a071ce17d9c78d905cb12

[root@ubuntu1804 ~]#docker port httpd01
80/tcp -> 0.0.0.0:32783

[root@ubuntu1804 ~]#docker inspect -f "{{.NetworkSettings.Networks.bridge.IPAddress}}" httpd01
172.17.0.2

#对应格式1
[root@ubuntu1804 ~]#docker inspect -f "{{.Config.Cmd}}" httpd01
[/bin/sh -c /bin/httpd -f -h /data/html]

#对应格式2
[root@ubuntu1804 ~]#docker inspect -f "{{.Config.Cmd}}" httpd01
[/bin/httpd -f -h /data/html]

[root@ubuntu1804 ~]#docker exec -it httpd01 sh
/ # pstree -p
httpd(1)

/ # ps aux
	PID   USER      TIME COMMAND
    1 	  root      0:00 /bin/httpd -fv -h /data/html
    7  	  root      0:00 sh
   13 	  root      0:00 ps aux
/ #

[root@ubuntu1804 ~]#curl 172.17.0.2
httpd website in busybox

[root@ubuntu1804 ~]#curl 127.0.0.1:32783
httpd website in busybox

#再次制作镜像v2.0版
[root@ubuntu1804 ~]#docker commit -a "test<root@www.test.com>" 35e985cf8f35 httpdbusybox:v2.0
[root@ubuntu1804 ~]#docker commit -a "test<root@www.test.com>" b1 httpdbusybox:v2.0
[root@ubuntu1804 ~]#docker run -d --name web2 -p 81:80 httpd-busybox:v2.0 /bin/httpd -fv -h /data/html
c47bce0de75dcdf88266467accbe0a119190999c23cec32d6af8b8500aed96d4

2.2.3 实战案例: 基于官方镜像生成的容器制作 tomcat 镜像

2.2.3.1 下载官方的tomcat镜像并运行

[root@ubuntu1804 ~]#docker images

[root@ubuntu1804 ~]#docker run -d -p 8080:8080 tomcat
Unable to find itony 'tomcat:latest' locally
latest: Pulling from library/tomcat
e9afc4f90ab0: Pull complete 
989e6b19a265: Pull complete
af14b6c2f878: Pull complete 
5573c4b30949: Pull complete 
fb1a405f128d: Pull complete 
612a9f566fdc: Pull complete 
4226f9b63dac: Pull complete 
cb8bfe875d7f: Pull complete 
5aa366608b6d: Pull complete 
4b7c9018ca5f: Pull complete 
Digest: sha256:46456ccf216f2fde198844d5c7d511f60e3ffc2ea3828e0cce9a2eed566e48e2
Status: Downloaded newer itony for tomcat:latest
6fcc5f7c1c7ce7be0b12007e310fc044e1077d489e1292b22621de55e302c455

[root@ubuntu1804 ~]#docker ps
CONTAINER ID       Itony               COMMAND             CREATED             
STATUS             PORTS                   NAMES
6fcc5f7c1c7c       tomcat              "catalina.sh run"   42 seconds ago     
Up 40 seconds       0.0.0.0:8080->8080/tcp   focused_wilbur
\
[root@ubuntu1804 ~]#curl -I 127.0.0.1:8080
HTTP/1.1 404
Content-Type: text/html;charset=utf-8
Content-Language: en
Transfer-Encoding: chunked
Date: Mon, 20 Jul 2020 15:33:21 GMT

2.2.3.2 修改容器

[root@ubuntu1804 ~]#docker exec -it 6fcc5f7c1c7c bash

root@6fcc5f7c1c7c:/usr/local/tomcat# ls
BUILDING.txt LICENSE README.md RUNNING.txt conf logs   temp     
webapps.dist
CONTRIBUTING.md NOTICE   RELEASE-NOTES bin     lib   native-jni-lib 
webapps work

root@6fcc5f7c1c7c:/usr/local/tomcat# ls webapps

root@6fcc5f7c1c7c:/usr/local/tomcat# ls webapps.dist/
ROOT docs examples host-manager manager

root@6fcc5f7c1c7c:/usr/local/tomcat# cp -a webapps.dist/* webapps/

root@6fcc5f7c1c7c:/usr/local/tomcat# ls webapps/
ROOT docs examples host-manager manager

root@6fcc5f7c1c7c:/usr/local/tomcat# exit
exit

[root@ubuntu1804 ~]#curl -I 127.0.0.1:8080
HTTP/1.1 200
Content-Type: text/html;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 20 Jul 2020 15:34:24 GMT

2.2.3.3 提交新镜像

[root@ubuntu1804 ~]#docker commit -m "add webapps app" -a "test" 6fcc5f7c1c7c tomcat:9.0.37-v1
sha256:b8d669ebf99e65d5ed69378d0d53f054e7de4865d335ab7aa0a7a5508e1369f7

[root@ubuntu1804 ~]#docker images 
REPOSITORY         TAG                 Itony ID           CREATED             
SIZE
tomcat              9.0.37-v1           b8d669ebf99e        4 seconds ago       
652MB
tomcat             latest             df72227b40e1        3 days ago         
647MB

[root@ubuntu1804 ~]#docker history tomcat:9.0.37-v1 
[root@ubuntu1804 ~]#docker inspect tomcat:9.0.37-v1 | tail -n20

#删除当前的容器
[root@ubuntu1804 ~]#docker rm -f 6fcc5f7c1c7c
6fcc5f7c1c7c

[root@ubuntu1804 ~]#docker ps 
CONTAINER ID       Itony               COMMAND             CREATED             
STATUS             PORTS               NAMES

2.2.3.4 利用新镜像启动容器

[root@ubuntu1804 ~]#docker run -d -p 8080:8080 --name tomcat tomcat:9.0.37-v1
97688b4c835da4b222f3ca15af36b8c27d4e60ff33480c314daf3f123b1cb50c

[root@ubuntu1804 ~]#docker ps 
CONTAINER ID       Itony               COMMAND             CREATED             
STATUS             PORTS                   NAMES
97688b4c835d       tomcat:9.0.37-v1    "catalina.sh run"   6 seconds ago       
Up 5 seconds        0.0.0.0:8080->8080/tcp   tomcat

2.2.3.5 测试新镜像启动的容器

浏览器访问 http://192.168.1.10:8080/ 可以看到下面显示

image-20211229110935189

2.2.4 实战案例: 基于Ubuntu的基础镜像利用 apt 安装手动制作nginx 的镜像

2.2.4.1 启动Ubuntu基础镜像并实现相关的配置

[root@ubuntu1804 ~]#docker run -it -p 80 --name nginx_ubuntu ubuntu bash

root@705148273eac:/# cat /etc/os-release 
NAME="Ubuntu"
VERSION="20.04 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacypolicy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal

root@705148273eac:/# ll /etc/apt/sources.list
-rw-r--r-- 1 root root 2743 Jul  3 02:00 /etc/apt/sources.list

root@705148273eac:/# cat > sources.list << EOF
deb http://repo.huaweicloud.com/ubuntu bionic main restricted
deb http://repo.huaweicloud.com/ubuntu bionic-updates main restricted
deb http://repo.huaweicloud.com/ubuntu bionic universe
deb http://repo.huaweicloud.com/ubuntu bionic-updates universe
deb http://repo.huaweicloud.com/ubuntu bionic multiverse
deb http://repo.huaweicloud.com/ubuntu bionic-updates multiverse
deb http://repo.huaweicloud.com/ubuntu bionic-security main restricted
deb http://repo.huaweicloud.com/ubuntu bionic-security universe
deb http://repo.huaweicloud.com/ubuntu bionic-security multiverse
EOF

root@705148273eac:/# apt update

root@705148273eac:/# apt -y install nginx

root@705148273eac:/# nginx -v 
nginx version: nginx/1.18.0 (Ubuntu)

root@705148273eac:~# grep include /etc/nginx/nginx.conf 
include /etc/nginx/modules-enabled/*.conf;
 include /etc/nginx/mime.types;
 include /etc/nginx/conf.d/*.conf;
 include /etc/nginx/sites-enabled/*;
 
root@705148273eac:~# grep root /etc/nginx/sites-enabled/default  
root /var/www/html;
# deny access to .htaccess files, if Apache's document root
# root /var/www/example.com;

root@705148273eac:/# echo Nginx Website in Docker > /var/www/html/index.html

root@705148273eac:/# exit
exit

2.2.4.2 提交为镜像

[root@ubuntu1804 ~]#docker commit -a 'test' -m 'nginx-ubuntu:20.04' nginx_ubuntu nginx_ubuntu20.04:v1.18.0
sha256:2c789ec21d2545c9bfc4af6d4380878153d52fcc03890aac755d09112631742a

[root@ubuntu1804 ~]#docker images  
REPOSITORY             TAG                 Itony ID           CREATED           SIZE
nginx_ubuntu20.04     v1.18.0             2c789ec21d25        22 seconds ago   179MB

2.2.4.3 从制作的新镜像启动容器并测试访问

[root@ubuntu1804 ~]#docker run -d -p 80 --name nginx-web nginx_ubuntu20.04:v1.18.0 nginx -g 'daemon off;'
b0c8496a497ba60f7b5bc430b075b00d40c7ace24068e71decac625e84df40de

[root@ubuntu1804 ~]#docker ps 
CONTAINER ID       Itony                       COMMAND                 CREATED 
            STATUS             PORTS                   NAMES
b0c8496a497b       nginx_ubuntu20.04:v1.18.0   "nginx -g 'daemon of…"   7
seconds ago       Up 5 seconds        0.0.0.0:32771->80/tcp   nginx-web

[root@ubuntu1804 ~]#docker port nginx-web
80/tcp -> 0.0.0.0:32771

[root@ubuntu1804 ~]#curl http://127.0.0.1:32771
Nginx Website in Docker

2.2.5 实战案例: 基于CentOS的基础镜像利用 yum 安装手动制作nginx 的镜像

2.2.5.1下载基础镜像并初始化系统

基于某个基础镜像之上重新制作,因此需要先有一个基础镜像,本次使用官方提供的centos镜像为基础

[root@ubuntu1804 ~]#docker pull centos:centos7.7.1908

[root@ubuntu1804 ~]#docker images 
REPOSITORY         TAG                 Itony ID           CREATED             SIZE
centos             centos7.7.1908     08d05d1d5859        2 months ago       204MB

[root@ubuntu1804 ~]#docker run -it centos:centos7.7.1908 bash

#修改时区
[root@9caa8742e6ce /]#rm -f /etc/localtime

[root@9caa8742e6ce /]#ln -s ../usr/share/zoneinfo/Asia/Shanghai /etc/localtime

[root@9caa8742e6ce /]# yum -y install wget 

[root@9caa8742e6ce /]# rm -rf /etc/yum.repos.d/*

#更改 yum 源
# -P 将文件保存到目录
[root@9caa8742e6ce /]# wget -P /etc/yum.repos.d/ http://mirrors.aliyun.com/repo/Centos-7.repo
[root@9caa8742e6ce /]# wget -P /etc/yum.repos.d/ http://mirrors.aliyun.com/repo/epel-7.repo

2.2.5.2 安装相关软件和工具

#yum安装nginx
[root@9caa8742e6ce /]# yum install nginx –y 

#安装常用命令
[root@9caa8742e6ce /]# yum install -y vim curl iproute net-tools 

#清理yum缓存
[root@9caa8742e6ce /]# rm -rf /var/cache/yum/*

2.2.5.3 修改服务的配置信息关闭服务后台运行

#关闭nginx后台运行
[root@9caa8742e6ce /]# vim /etc/nginx/nginx.conf 
user nginx;
daemon off; #关闭后台运行

2.2.5.4 准备程序和数据

#自定义web界面
[root@9caa8742e6ce ~]# rm -f /usr/share/nginx/html/index.html

[root@9caa8742e6ce ~]# echo "Nginx Page in Docker" > /usr/share/nginx/html/index.html

2.2.5.5 提交为镜像

docker commit 命令在宿主机基于容器ID 提交为镜像

#不关闭容器的情况,将容器提交为镜像
[root@ubuntu1804 ~]#docker commit -a "root@cool1.com" -m "nginx yum v1" -c "EXPOSE 80 443" 9caa8742e6ce test/centos7-nginx:1.16.1.v1
sha256:e9d09cc585ed8ee1544b1e68de326ea6dcbe99577fc9b2edad9ab481b7a7e7ec

[root@ubuntu1804 ~]#docker images 
REPOSITORY           TAG                 Itony ID           CREATED             
SIZE
test/centos7-nginx   1.16.1.v1           e9d09cc585ed        4 seconds ago     442MB
centos               centos7.7.1908     08d05d1d5859        2 months ago       204MB

2.2.5.6 从制作的镜像启动容器

[root@ubuntu1804 ~]#docker run -d -p 8080:80 --name my-centos-nginx test/centos7-nginx:1.16.1.v1 /usr/sbin/nginx 
c60f8373a14210bb3aa06ce03c2258a4b912033b0650ef690f9245fc3afc5bf1

[root@ubuntu1804 ~]#docker ps 
CONTAINER ID       Itony                         COMMAND             CREATED   
          STATUS             PORTS                           NAMES
c60f8373a142       test/centos7-nginx:1.16.1.v1   "/usr/sbin/nginx"   6 seconds 
ago       Up 5 seconds        443/tcp, 0.0.0.0:8080->80/tcp   my-centos-nginx
9caa8742e6ce       centos:centos7.7.1908          "bash"              35
minutes ago     Up 35 minutes                                       
thirsty_hypatia

[root@ubuntu1804 ~]#

2.2.5.7 访问测试镜像

[root@ubuntu1804 ~]#curl 127.0.0.1:8080
Nginx Page in Docker

[root@ubuntu1804 ~]#

2.2.6 实战案例: 基于CentOS 基础镜像手动制作编译版本 nginx 镜像

在CentOS 基础镜像的容器之上手动编译安装nginx,然后再将此容器提交为镜像

2.2.6.1 下载镜像并初始化系统

[root@ubuntu1804 ~]#docker pull centos:centos7.7.1908
[root@ubuntu1804 ~]#docker images 
REPOSITORY         TAG                 Itony ID           CREATED             
SIZE
centos             centos7.7.1908     08d05d1d5859        2 months ago       
204MB
[root@ubuntu1804 ~]#docker run -it centos:centos7.7.1908 /bin/bash

#生成yum源配置
[root@86a48908bb97 /]# yum -y install wget

[root@64944257fa88 /]# rm -rf /etc/yum.repos.d/*

[root@64944257fa88 /]# wget -P /etc/yum.repos.d/ http://mirrors.aliyun.com/repo/Centos-7.repo 
[root@64944257fa88 /]# wget -P /etc/yum.repos.d/ http://mirrors.aliyun.com/repo/epel-7.repo

2.2.6.2 编译安装 nginx

[root@64944257fa88 /]# useradd -r -s /sbin/nologin nginx

#安装基础包
[root@64944257fa88 /]# yum -y install gcc gcc-c++ automake pcre pcre-devel zlib zlib-devel openssl openssl-devel

[root@64944257fa88 /]# cd /usr/local/src

[root@64944257fa88 src]# wget http://nginx.org/download/nginx-1.16.1.tar.gz

[root@64944257fa88 src]# tar xf nginx-1.16.1.tar.gz

[root@64944257fa88 src]# cd nginx-1.16.1

[root@64944257fa88 nginx-1.16.1]# ./configure --prefix=/apps/nginx

[root@64944257fa88 nginx-1.16.1]# make && make install

[root@64944257fa88 nginx-1.16.1]# rm -rf /var/cache/yum/*

2.2.6.3 关闭 nginx 后台运行

[root@64944257fa88 nginx-1.16.1]# cd /apps/nginx/  

[root@64944257fa88 nginx]# ls
conf html logs sbin

[root@64944257fa88 nginx]# vi conf/nginx.conf
user nginx;
daemon off;

[root@64944257fa88 nginx]# ln -s /apps/nginx/sbin/nginx /usr/sbin/

[root@64944257fa88 nginx]# ll /usr/sbin/nginx 
lrwxrwxrwx 1 root root 22 Jan 28 05:29 /usr/sbin/nginx -> /apps/nginx/sbin/nginx

2.2.6.4 准备相关数据自定义web界面

[root@64944257fa88 nginx]# echo "Nginx Test Page in Docker" > /apps/nginx/html/index.html

2.2.6.5 提交为镜像

#不要退出容器,在另一个终端窗口执行以下命令
[root@ubuntu1804 ~]#docker images 
REPOSITORY         TAG                 Itony ID           CREATED            SIZE
centos             centos7.7.1908     08d05d1d5859        2 months ago       204MB

[root@ubuntu1804 ~]#docker ps 
CONTAINER ID       Itony                   COMMAND             CREATED         
    STATUS             PORTS               NAMES
64944257fa88       centos:centos7.7.1908   "/bin/bash"         18 seconds ago   
  Up 17 seconds                           stupefied_albattani

[root@ubuntu1804 ~]#docker commit -m "nginx1.6.1" -c "CMD nginx" 5d967919027c centos7-nginx:1.24.0
sha256:d86d957bb00f35fe09ae38230e1e2d12916f4406e997146c68e34dae7526c079

[root@ubuntu1804 ~]#docker images 
REPOSITORY         TAG                 Itony ID           CREATED             
SIZE
centos7-nginx       1.6.1               d86d957bb00f        2 minutes ago    486MB
centos             centos7.7.1908     08d05d1d5859        2 months ago       204MB

2.2.6.6 从自己的镜像启动容器

[root@ubuntu1804 ~]#docker run -d -p 80:80 centos7-nginx:1.6.1 nginx
ae90b1abf374138a21f7ed104d14c88f1af23c0b2027c3fe099722fd7fbad3a4

[root@ubuntu1804 ~]#docker ps 
CONTAINER ID       Itony                 COMMAND             CREATED           
  STATUS             PORTS               NAMES
ae90b1abf374       centos7-nginx:1.6.1   "nginx"             About a minute ago 
  Up About a minute   0.0.0.0:80->80/tcp   naughty_thompson

备注: 最后面的nginx是运行的命令,即镜像里面要运行一个nginx命令,所以前面软链接到/usr/sbin/nginx,目的为了让系统不需要指定路径就可以执行此命令.

2.2.6.7 访问测试

[root@ubuntu1804 ~]#curl 127.0.0.1
Nginx Test Page in Docker

2.2.6.8 查看Nginx访问日志和进程

[root@ubuntu1804 ~]#docker exec -it ae90b1abf374 bash

[root@ae90b1abf374 /]# cat /apps/nginx/logs/access.log 
172.17.0.1 - - [28/Jan/2020:05:40:51 +0000] "GET / HTTP/1.1" 200 26 "-""curl/7.58.0"

[root@ae90b1abf374 /]# ps aux
USER         PID  %CPU %MEM   VSZ   RSS  TTY     STAT START   TIME COMMAND
root          1   0.0  0.2   20572  2468  ?       Ss   05:40   0:00 nginx: master process nginx
nginx         6   0.0  0.3   21024  3104  ?       S    05:40   0:00 nginx: worker process
root          7   0.3  0.2   11840  2928  pts/0   Ss   05:45   0:00 bash
root         21   0.0  0.3   51764  3344  pts/0   R+   05:46   0:00 ps aux

2.3 利用 DockerFile 文件执行 docker build 自动构建镜像

2.3.1 Dockfile 使用详解

2.3.1.1 Dockerfile 介绍

DockerFile 是一种被Docker程序解释执行的脚本,由一条条的命令组成的,每条命令对应linux下面的一条命令,Docker程序将这些DockerFile指令再翻译成真正的linux命令,其有自己的书写方式和支持的命令,Docker程序读取DockerFile并根据指令生成Docker镜像,相比手动制作镜像的方式,DockerFile更能直观的展示镜像是怎么产生的,有了DockerFile,当后期有额外的需求时,只要在之前 的DockerFile添加或者修改响应的命令即可重新生成新的Docker镜像,避免了重复手动制作镜像的麻烦,类似与shell脚本一样,可以方便高效的制作镜像

Docker守护程序 Dockerfile 逐一运行指令,如有必要,将每个指令的结果提交到新镜像,然后最终输出新镜像的ID。Docker守护程序将自动清理之前发送的上下文

请注意,每条指令都是独立运行的,并会导致创建新镜像,比如 RUN cd /tmp 对下一条指令不会有任何影响。

Docker将尽可能重用中间镜像层(缓存),以显著加速 docker build 命令的执行过程,这由 Using cache 控制台输出中的消息指示

2.3.1.2 Dockerfile 镜像制作和使用流程

itony-20211229205354814

2.3.1.3 Dockerfile文件的制作镜像的分层结构

itony-20211229205450754

范例:

#按照业务类型或系统类型等方式划分创建目录环境,方便后期镜像比较多的时候进行分类
[root@ubuntu1804 ~]#mkdir /data/dockerfile/{web/{nginx,apache,tomcat,jdk},system/{centos,ubuntu,alpine,debian}} -p

[root@ubuntu1804 ~]#tree /data/dockerfile/
/data/dockerfile/
├── system
│   ├── alpine
│   ├── centos
│   ├── debian
│   └── ubuntu
└── web
   ├── apache
   ├── jdk
   ├── nginx
   └── tomcat
10 directories, 0 files
[root@ubuntu1804 ~]#

2.3.1.4 Dockerfile 文件格式

Dockerfile 是一个有特定语法格式的文本文件

dockerfile 官方说明: https://docs.docker.com/engine/reference/builder/

帮助: man 5 dockerfile

Dockerfile 文件说明

  • 每一行以Dockerfile的指令开头,指令不区分大小写,但是惯例使用大写
  • 使用 # 开始作为注释
  • 每一行只支持一条指令,每条指令可以携带多个参数
  • 指令按文件的顺序从上至下进行执行
  • 每个指令的执行会生成一个新的镜像层,为了减少分层和镜像大小,尽可能将多条指令合并成一条指令
  • 制作镜像一般可能需要反复多次,每次执行dockfile都按顺序执行,从头开始,已经执行过的指令已经缓存,不需要再执行,如果后续有一行新的指令没执行过,其往后的指令将会重新执行,所以为加速镜像制作,将最常变化的内容放下dockerfile的文件的后面

2.3.1.5 Dockerfile 相关指令

dockerfile 文件中的常见指令:

ADD
COPY
ENV
EXPOSE
FROM
LABEL
STOPSIGNAL
USER
VOLUME
WORKDIR
2.3.1.5.1 FROM: 指定基础镜像

定制镜像,需要先有一个基础镜像,在这个基础镜像上进行定制。

FROM 就是指定基础镜像,此指令通常必需放在Dockerfile文件第一个非注释行。后续的指令都是运行于此基准镜像所提供的运行环境

基础镜像可以是任何可用镜像文件,默认情况下,docker build会在docker主机上查找指定的镜像文件,在其不存在时,则会从Docker Hub Registry上拉取所需的镜像文件.如果找不到指定的镜像文件,docker build会返回一个错误信息

如何选择合适的镜像呢?

对于不同的软件官方都提供了相关的docker镜像,比如: nginx、redis、mysql、httpd、tomcat等服务类的镜像,也有操作系统类,如: centos、ubuntu、debian等。建议使用官方镜像,比较安全。

格式:

FROM [--platform=<platform>] <itony> [AS <name>]
FROM [--platform=<platform>] <itony>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <itony>[@<digest>] [AS <name>]
#说明:  
--platform 指定镜像的平台,比如: linux/amd64, linux/arm64, or windows/amd64
tag 和 digest是可选项,如果不指定,默认为latest

说明: 关于scratch 镜像

FROM scratch
参考链接:
https://hub.docker.com/_/scratch?tab=description
https://docs.docker.com/develop/develop-images/baseimages/
该镜像是一个空的镜像,可以用于构建busybox等超小镜像,可以说是真正的从零开始构建属于自己的镜像
该镜像在构建基础镜像(例如debian和busybox)或超最小镜像(仅包含一个二进制文件及其所需内容,例
如:hello-world)的上下文中最有用。

范例:

FROM scratch #所有镜像的起源镜像,相当于Object类
FROM ubuntu
FROM ubuntu:bionic
FROM debian:buster-slim
2.3.1.5.2 LABEL: 指定镜像元数据

可以指定镜像元数据,如: 镜像作者等

LABEL <key>=<value> <key>=<value> <key>=<value> ...

范例:

LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."

一个镜像可以有多个label ,还可以写在一行中,即多标签写法,可以减少镜像的的大小

范例: 多标签写法

#一行格式
LABEL multi.label1="value1" multi.label2="value2" other="value3"
#多行格式
LABEL multi.label1="value1" \
      multi.label2="value2" \
      other="value3"

docker inspect 命令可以查看LABEL

范例:

"Labels": {
    "com.example.vendor": "ACME Incorporated"
    "com.example.label-with-value": "foo",
    "version": "1.0",
    "description": "This text illustrates that label-values can span multiple lines.",
    "multi.label1": "value1",
    "multi.label2": "value2",
    "other": "value3"
},

MAINTAINER: 指定维护者信息

此指令已过时,用LABEL代替

MAINTAINER <name>

范例:

MAINTAINER testdemo <root@testdemo.com>
#用LABEL代替
LABEL maintainer="testdemo <root@testdemo.com>"
2.3.1.5.3 RUN: 执行 shell命令

RUN 指令用来在构建镜像阶段需要执行 FROM 指定镜像所支持的Shell命令。

通常各种基础镜像一般都支持丰富的shell命令

注意: RUN 可以写多个,每一个RUN指令都会建立一个镜像层,所以尽可能合并成一条指令,比如将多

个shell命令通过 && 连接一起成为在一条指令

每个RUN都是独立运行的,和前一个RUN无关

#shell 格式: 相当于 /bin/sh -c <命令> 此种形式支持环境变量
RUN <命令> 

#exec 格式: 此种形式不支持环境变量,注意:是双引号,不能是单引号
RUN ["可执行文件", "参数1", "参数2"]

#exec格式可以指定其它shell
RUN ["/bin/bash","-c","echo hello cool"]

说明:

shell格式中,<command>通常是一个shell命令,且以"/bin/sh -c”来运行它,这意味着此进程在容器中的PID不为1,不能接收Unix信号,因此,当使用docker stop <container>命令停止容器时,此进程接收不到SIGTERM信号

exec格式中的参数是一个JSON格式的数组,其中<executable>为要运行的命令,后面的<paramN>为传递给命令的选项或参数;然而,此种格式指定的命令不会以"/bin/sh -c"来发起,因此常见的shell操作如变量替换以及通配符(?,*等)替换将不会进行;不过,如果要运行的命令依赖于此shell特性的话,可以将其替换为类似下面的格式。
RUN ["/bin/bash", "-c", "<executable>", "<param1>"]

范例:

RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
RUN ["/bin/bash", "-c", "echo hello world"]
RUN yum -y install epel-release \
     && yum -y install nginx \
     && rm -rf /usr/share/nginx/html/*
     && echo "<h1> docker test nginx </h1>" > /usr/share/nginx/html/index.html

范例: 多个 前后 RUN 命令独立无关和shell命令不同

#world.txt并不存放在/app内
RUN cd /app
RUN echo "hello" > world.txt
2.3.1.5.4 ENV: 设置环境变量

ENV 可以定义环境变量和值,会被后续指令(如:ENV,ADD,COPY,RUN等)通过$KEY或${KEY}进行引用,并在容器运行时保持

#变量赋值格式1
ENV <key> <value>   #此格式只能对一个key赋值,<key>之后的所有内容均会被视作其<value>的组成
部分
#变量赋值格式2
ENV <key1>=<value1> <key2>=<value2> \  #此格式可以支持多个key赋值,定义多个变量建议使用,
减少镜像层
 <key3>=<value3> ...
 
#如果<value>中包含空格,可以以反斜线\进行转义,也可通过对<value>加引号进行标识;另外,反斜线也
可用于续行
#只使用一次变量
RUN <key>=<value> <command>
    
#引用变量
RUN $key .....
#变量支持高级赋值格式
${key:-word}
${kye:+word}

如果运行容器时如果需要修改变量,可以执行下面通过基于 exec 机制实现

注意: 下面方式只影响容器运行时环境,而不影响构建镜像的过程,即只能覆盖docker run时的环境变量,而不会影响docker build时环境变量的值

docker run -e|--env <key>=<value>
#说明
-e, --env list   #Set environment variables
    --env-file filename     #Read in a file of environment variables

示例: 两种格式功能相同

#格式1
ENV myName="John Doe" myDog=Rex\ The\ Dog \
    myCat=fluffy
    
#格式2
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy

范例:

ENV VERSION=1.0 DEBUG=on NAME="Happy Feet"
ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC
/usr/src/postgress && …
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH

范例:

[root@ubuntu1804 dockerfile]#cat Dockerfile
FROM busybox
LABEL maintainer="testdemo <root@testdemo.com>"
ENV NAME cool test server
RUN touch $NAME.txt

[root@ubuntu1804 dockerfile]#cat build.sh 
#!/bin/bash
#
TAG=$1
docker build -t test:$TAG .
[root@ubuntu1804 dockerfile]#./build.sh v5.0
[root@ubuntu1804 dockerfile]#docker run --rm --name c1 test:v5.0 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=d4e1f89aca71
NAME=cool xiao chun
HOME=/root
[root@ubuntu1804 dockerfile]#docker run --rm -e NAME=tony --name c1 test:v5.0 
env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=b23500aa100d
NAME=tony
HOME=/root
[root@ubuntu1804 dockerfile]#docker run --rm -e NAME=tony --name c1 test:v5.0 ls -l
[root@ubuntu1804 dockerfile]#cat env.txt
NAME=cool
TITLE=cto
[root@ubuntu1804 dockerfile]#docker run --rm --env-file env.txt --name c1 test:v5.0 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=88ddf5b73a6b
NAME=cool
TITLE=cto
HOME=/root
2.3.1.5.5 COPY: 复制文本

复制本地宿主机的 到容器中的 。

COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"] #路径中有空白字符时,建议使用此格式

说明:

  • 可以是多个,可以使用通配符,通配符规则满足Go的filepath.Match 规则filepath.Match 参考链接: https://golang.org/pkg/path/filepath/#Match
  • 必须是build上下文中的路径(为 Dockerfile 所在目录的相对路径),不能是其父目录中的文件
  • 如果是目录,则其内部文件或子目录会被递归复制,但目录自身不会被复制
  • 如果指定了多个, 或在中使用了通配符,则必须是一个目 录,且必须以 / 结尾
  • 可以是绝对路径或者是 WORKDIR 指定的相对路径
  • 使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等
  • 如果事先不存在,它将会被自动创建,这包括其父目录路径,即递归创建目录

范例:

COPY hom* /mydir/    
COPY hom?.txt /mydir/
2.3.1.5.6 ADD: 复制和解包文件

该命令可认为是增强版的COPY,不仅支持COPY,还支持自动解缩。可以将复制指定的到容器中的

ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"] 

说明:

  • 可以是Dockerfile所在目录的一个相对路径;也可是一个 URL;还可是一个 tar 文件(自动解压)
  • 可以是绝对路径或者是 WORKDIR 指定的相对路径
  • 如果是目录,只复制目录中的内容,而非目录本身
  • 如果是一个 URL ,下载后的文件权限自动设置为 600
  • 如果为URL且不以/结尾,则指定的文件将被下载并直接被创建为,如果以 / 结尾,则文件名URL指定的文件将被直接下载并保存为/< filename>
  • 如果是一个本地文件系统上的打包文件,如: gz, bz2 ,xz ,它将被解包 ,其行为类似于"tar -x"命令,但是通过URL获取到的tar文件将不会自动展开
  • 如果有多个,或其间接或直接使用了通配符,则必须是一个以/结尾的目录路径;如果不以/结尾,则其被视作一个普通文件,的内容将被直接写入到

范例:

ADD test relativeDir/          # adds "test" to `WORKDIR`/relativeDir/
ADD test /absoluteDir/         # adds "test" to /absoluteDir/
ADD --chown=55:mygroup files* /somedir/
ADD --chown=bin files* /somedir/
ADD --chown=1 files* /somedir/
ADD --chown=10:11 files* /somedir/
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /
2.3.1.5.7 CMD: 容器启动命令

image-20211229220344571

一个容器中需要持续运行的进程一般只有一个,CMD 用来指定启动容器时默认执行的一个命令,且其运行结束后,容器也会停止,所以一般CMD 指定的命令为持续运行且为前台命令。

  • 如果docker run没有指定任何的执行命令或者dockerfile里面也没有ENTRYPOINT,那么开启容器时就会使用执行CMD指定的默认的命令
  • 前面介绍过的 RUN 命令是在构建镜像进执行的命令,注意二者的不同之处
  • 每个 Dockerfile 只能有一条 CMD 命令。如指定了多条,只有最后一条被执行
  • 如果用户启动容器时用 docker run xxx 指定运行的命令,则会覆盖 CMD 指定的命令
# 使用 exec 执行,推荐方式,第一个参数必须是命令的全路径,此种形式不支持环境变量
CMD ["executable","param1","param2"] 

# 在 /bin/sh 中执行,提供给需要交互的应用;此种形式支持环境变量
CMD command param1 param2 

# 提供给 ENTRYPOINT 命令的默认参数
CMD ["param1","param2"] 

范例:

CMD ["nginx", "-g", "daemon off;"]

范例:

[root@ubuntu1804 dockerfile]#cat Dockerfile 
FROM ubuntu:18.04
LABEL maintainer="zhou <root@zhou.com>"
RUN apt update \
&& apt -y install  curl \
&& rm -rf /var/lib/apt/lists/*
CMD [ "curl", "-s","https://ip.cn"]

[root@centos8 ubuntu]#podman run 9b
{"ip": "111.199.187.36", "country": "北京市", "city": "联通"}

#cat /etc/etc/issue覆盖了curl命令
[root@centos8 ubuntu]#podman run 9b cat /etc/issue
Ubuntu 18.04.4 LTS \n \l

范例:

[root@ubuntu1804 dockerfile]#pwd
/data/dockerfile

[root@ubuntu1804 dockerfile]#cat Dockerfile 
FROM busybox
LABEL maintainer="zhou <root@zhou.com>"
ENV ROOT /data/website
COPY index.html ${ROOT}/index.html
CMD /bin/httpd -f -h ${ROOT}
EXPOSE 80

[root@ubuntu1804 dockerfile]#cat index.html 
Website in Dockerfile

[root@ubuntu1804 dockerfile]#cat build.sh 
#!/bin/bash
#
TAG=$1
docker build -t test:$TAG .

[root@ubuntu1804 dockerfile]#./build.sh v1.0

[root@ubuntu1804 dockerfile]#docker run -d --rm -P --name c1 test:v1.0 

[root@ubuntu1804 ~]#docker port c1
80/tcp -> 0.0.0.0:32781

[root@ubuntu1804 ~]#curl 127.0.0.1:32781
Website in Dockerfile

范例:

[root@ubuntu1804 dockerfile]#pwd
/data/dockerfile

[root@ubuntu1804 dockerfile]#cat Dockerfile 
FROM busybox
LABEL maintainer="zhou <root@zhou.com>"
ENV ROOT /data/website
RUN mkdir -p ${ROOT} && echo '<h1> Busybox httpd server in Dockerfile</h1>' > 
${ROOT}/index.html
#COPY index.html ${ROOT}/index.html
CMD ["/bin/sh","-c","/bin/httpd -f -h ${ROOT}"]
#CMD /bin/httpd -f -h ${ROOT}
EXPOSE 80

[root@ubuntu1804 dockerfile]#cat build.sh 
#!/bin/bash
#
TAG=$1
docker build -t test:$TAG .

[root@ubuntu1804 dockerfile]#ls
build.sh Dockerfile

[root@ubuntu1804 dockerfile]#./build.sh v2.0

[root@ubuntu1804 dockerfile]#docker run -d --rm -P --name c2 test:v2.0 
cf5fea09ce1362f18a90f4fcdffa3000de26b4919927e1cdce0cce6243f5dd24

[root@ubuntu1804 dockerfile]#docker port c2
80/tcp -> 0.0.0.0:32785

[root@ubuntu1804 dockerfile]#curl 127.0.0.1:32785
<h1> Busybox httpd server in Dockerfile</h1>

[root@ubuntu1804 dockerfile]#docker inspect -f "{{.Config}}" test:v2.0
{   false false false map[80/tcp:{}] false false false
[PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 
ROOT=/data/website] [/bin/sh -c /bin/httpd -f -h ${ROOT}] <nil> false
sha256:d22731d921df65f9da7452e714cf35ef345529ffa1bd5b92c0024d1d1bd8e714 map[] 
[] false [] map[] <nil> []}

#查看进程关系
[root@ubuntu1804 ~]#docker exec -it c2 sh
/ # ps
PID   USER     TIME COMMAND
    1 root      0:00 /bin/httpd -f -h /data/website
    8 root      0:00 sh
   13 root      0:00 ps
2.3.1.5.8 ENTRYPOINT: 入口点

功能类似于CMD,配置容器启动后执行的命令及参数

# 使用 exec 执行
ENTRYPOINT ["executable", "param1", "param2"]

# shell中执行
ENTRYPOINT command param1 param2
  • ENTRYPOINT 不能被 docker run 提供的参数覆盖,而是追加,即如果docker run 命令有参数,那么参数全部都会作为ENTRYPOINT的参数
  • 如果docker run 后面没有额外参数,但是dockerfile中的CMD里有(即上面CMD的第三种用法),即Dockerfile中即有CMD也有ENTRYPOINT,那么CMD的全部内容会作为ENTRYPOINT的参数
  • 如果docker run 后面有额外参数,同时Dockerfile中即有CMD也有ENTRYPOINT,那么docker run后面的参数覆盖掉CMD参数内容,最终作为ENTRYPOINT的参数
  • 可以通过docker run --entrypoint string 参数在运行时替换,注意string不要加空格
  • 使用CMD要在运行时重新写命令本身,然后在后面才能追加运行参数,ENTRYPOINT则可以运行时无需重写命令就可以直接接受新参数
  • 每个 Dockerfile 中只能有一个 ENTRYPOINT,当指定多个时,只有最后一个生效

范例:

[root@ubuntu1804 ~]#docker run -it --entrypoint cat alpine /etc/issue
Welcome to Alpine Linux 3.12
Kernel \r on an \m (\l)

范例:

[root@ubuntu1804 dockerfile]#cat Dockerfile
FROM ubuntu:18.04
RUN apt update \
&& apt -y install  curl \
&& rm -rf /var/lib/apt/lists/*
ENTRYPOINT [ "curl", "-s","https://ip.cn"]
[root@centos8 dockerfile]#podman run -it --rm f68e006 
{"ip": "111.199.187.36", "country": "北京市", "city": "联通"}

#追加-i参数
[root@centos8 dockerfile]#podman run -it --rm f68e006 -i
HTTP/2 200
date: Sun, 23 Feb 2020 08:05:19 GMT
content-type: application/json; charset=UTF-8
set-cookie: __cfduid=d4a22496ea6f3b2861763354f8ca600711582445119; expires=Tue, 24-Mar-20 08:05:19 GMT; path=/; domain=.ip.cn; HttpOnly; SameSite=Laxcf-cache-status: DYNAMIC
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdncgi/beacon/expect-ct"
alt-svc: h3-25=":443"; ma=86400, h3-24=":443"; ma=86400, h3-23=":443"; ma=86400
server: cloudflare
cf-ray: 5697b1ac1862eb41-LAX
{"ip": "111.199.187.36", "country": "北京市", "city": "联通"}

范例: 利用脚本实现指定环境变量动态生成配置文件内容

[root@ubuntu1804 ~]#echo 'Nginx Website in Dockerfile' > index.html
[root@ubuntu1804 ~]#cat Dockerfile
FROM nginx:1.16-alpine
LABEL maintainer="test <root@test.com>"
ENV DOC_ROOT='/data/website/'
ADD index.html ${DOC_ROOT}
ADD entrypoint.sh /bin/
EXPOSE 80/tcp 8080
#HEALTHCHECK --start-period=3s CMD wget -0 - -q http://${IP:-0.0.0.0}:{PORT:-80}/
CMD ["/usr/sbin/nginx","-g", "daemon off;"]  #CMD指令的内容都成为了ENTRYPOINT的参数
ENTRYPOINT [ "/bin/entrypoint.sh"]

[root@ubuntu1804 ~]#cat entrypoint.sh
#!/bin/sh 
cat > /etc/nginx/conf.d/www.conf <<EOF
server {
   server_name ${HOSTNAME};
   listen ${IP:-0.0.0.0}:${PORT:-80};
   root   ${DOC_ROOT:-/usr/share/nginx/html};
}
EOF
exec "$@"

[root@ubuntu1804 ~]#chmod +x entrypoint.sh
[root@ubuntu1804 ~]#docker build -t nginx:v1.0 . 
[root@ubuntu1804 ~]#docker run --name n1 --rm -P -e "PORT=8080" -e "HOSTNAME=www.test.com" nginx:v1.0
2.3.1.5.9 ARG: 构建参数

ARG指令在build 阶段指定变量,和ENV不同的是,容器运行时不会存在这些环境变量

ARG <name>[=<default value>]

如果和ENV同名,ENV覆盖ARG变量

可以用 docker build --build-arg <参数名>=<值> 来覆盖

范例:

[root@ubuntu1804 ~]#cat Dockerfile
FROM busybox
ARG author="zhou <root@zhou.com>"
LABEL maintainer="${author}"
[root@ubuntu1804 ~]#docker build --build-arg author="123456@qq.com" -t busybox:v1.0 . 

说明: ARG 和 FROM

#FROM指令支持ARG指令放在第一个FROM之前声明变量
#示例:
ARG  CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD /code/run-app

FROM extras:${CODE_VERSION}
CMD /code/run-extras
#在FROM之前声明的ARG在构建阶段之外,所以它不能在FROM之后的任何指令中使用。 要使用在第一个FROM之前声明的ARG的默认值,请在构建阶段内使用没有值的ARG指令

#示例:
ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version
2.3.1.5.11 VOLUME: 匿名卷

在容器中创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等,默认会将宿主机上的目录挂载至VOLUME 指令指定的容器目录。即使容器后期被删除,此宿主机的目录仍会保留,从而实现容器数据的持久保存。

宿主机目录为

/var/lib/docker/volumes/<volume_id>/_data

语法:

VOLUME <容器内路径>
VOLUME ["<容器内路径1>", "<容器内路径2>"...]
    
注意: 
<容器内路径>如果在容器内不存在,在创建容器时会自动创建
<容器内路径>如果是存在的,同时目录内有内容,将会把此目录的内容复制到宿主机的实际目录

注意:

  • Dockerfile中的VOLUME实现的是匿名数据卷,无法指定宿主机路径和容器目录的挂载关系
  • 通过docker rm -fv <容器ID> 可以删除容器的同时删除VOLUME指定的卷

范例: 在容器创建两个/data/ ,/data2的挂载点

VOLUME [ "/data1","/data2" ]  

范例:

[root@centos8 ~]#cat /data/dockerfile/system/alpine/Dockerfile 
FROM alpine:3.11 
LABEL maintainer="zhou <root@zhou.com>"
COPY repositories /etc/apk/repositories 
VOLUME [ "/testdata","/testdata2" ]

[root@centos8 alpine]#podman run -it --rm 8ef61dd3959da3f sh
/ # df 
Filesystem           1K-blocks     Used Available Use% Mounted on
overlay              104806400   3656380 101150020   3% /
tmpfs                    65536         0     65536   0% /dev
/dev/sda2            104806400   3656380 101150020   3% /testdata2
/dev/sda2            104806400   3656380 101150020   3% /testdata
/ # cp /etc/issue /testdata/f1.txt
/ # cp /etc/issue /testdata2/f2.txt
[root@centos8 ~]#tree /var/lib/containers/storage/volumes/
/var/lib/containers/storage/volumes/
├── 725f0f67921bdbffbe0aaf9b015d663a6e3ddd24674990d492025dfcf878529b
│   └── _data
│       └── f1.txt
└── fbd13e5253deb375e0dea917df832d2322e96b04ab43bae061584dcdbe7e89f2
   └── _data
       └── f2.txt
4 directories, 2 files
2.3.1.5.12 EXPOSE: 暴露端口

指定服务端的容器需要对外暴露(监听)的端口号,以实现容器与外部通信。

EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会真正暴露端口,即不会自动在宿主进行端口映射。

因此,在启动容器时需要通过 -P 或 -p ,Docker 主机才会真正分配一个端口转发到指定暴露的端口才可使用。

注意: 即使 Dockerfile 没有 EXPOSE 端口指令,也可以通过docker run -p 临时暴露容器内程序真正监听的端口,所以EXPOSE 相当于指定默认的暴露端口,可以通过docker run -P 进行真正暴露。

EXPOSE <port>[/ <protocol>] [<port>[/ <protocol>] ..]
    
#说明
<protocol>用于指定传输层协议,可为tcp或udp二者之一,默认为TCP协议

范例:

EXPOSE 80 443
EXPOSE 11211/udp 11211/tcp

范例:

[root@ubuntu1804 dockerfile]#pwd
/data/dockerfile
[root@ubuntu1804 dockerfile]#echo Website in Dockerfile > index.html
[root@ubuntu1804 dockerfile]#vim Dockerfile
FROM busybox
LABEL maintainer="zhou <root@zhou.com>"
COPY index.html /data/website/
EXPOSE 80
[root@ubuntu1804 dockerfile]#cat build.sh 
#!/bin/bash
#
TAG=$1
docker build -t test:$TAG .
[root@ubuntu1804 dockerfile]#chmod +x build.sh
[root@ubuntu1804 dockerfile]#./build.sh v1.0
[root@ubuntu1804 dockerfile]#ls
build.sh Dockerfile index.html
[root@ubuntu1804 dockerfile]#docker run --rm -P --name c1 test:v1.0 /bin/httpd -f -h /data/website
[root@ubuntu1804 ~]#docker port c1
80/tcp -> 0.0.0.0:32773
[root@ubuntu1804 ~]#curl 127.0.0.1:32773
Website in Dockerfile
[root@ubuntu1804 ~]#docker kill c1
c1
2.3.1.5.13 WORKDIR: 指定工作目录

为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录,当容器运行后,进入容器内WORKDIR指定的默认目录

WORKDIR 指定工作目录(或称当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会自行创建

WORKDIR /path/to/workdir

范例:

#两次RUN独立运行,不在同一个目录
RUN cd /app
RUN echo "hello" > world.txt

#如果想实现相同目录可以使用WORKDIR
WORKDIR /app
RUN echo "hello" > world.txt

可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

#则最终路径为 /a/b/c
2.3.1.5.14 ONBUILD: 子镜像引用父镜像的指令

可以用来配置当构建当前镜像的子镜像时,会自动触发执行的指令,但在当前镜像构建时,并不会执行,即延迟到子镜像构建时才执行

ONBUILD [INSTRUCTION]

例如,Dockerfile 使用如下的内容创建了镜像 image-A。

...
ONBUILD ADD http://www.ops.com/wp-content/uploads/2024/02/logo.png /data/
ONBUILD RUN rm -rf /*
ONBUILD RUN /usr/local/bin/python-build --dir /app/src...

如果基于 image-A 创建新的镜像image-B时,新的Dockerfile中使用 FROM image-A指定基础镜像时,会自动执行ONBUILD 指令内容,等价于在后面添加了两条指令。

FROM image-A
#Automatically run the following
ADD http://www.ops.com/wp-content/uploads/2024/02/logo.png /data/
RUN /usr/local/bin/python-build --dir /app/src

说明:

  • 尽管任何指令都可注册成为触发器指令,但ONBUILD不能自我能套,且不会触发FROM和MAINTAINER指令
  • 使用 ONBUILD 指令的镜像,推荐在标签中注明,例如 ruby:1.9-onbuild
2.3.1.5.15 USER: 指定当前用户

指定运行容器的用户名或 UID,在后续dockerfile中的 RUN ,CMD和ENTRYPOINT指令时使用此用户

当服务不需要管理员权限时,可以通过该命令指定运行用户

这个用户必须是事先建立好的,否则无法切换

如果没有指定 USER,默认是 root 身份执行

USER <user>[:<group>] 
USER <UID>[:<GID>]

范例:

RUN groupadd -r mysql && useradd -r -g mysql mysql
USER mysql
2.3.1.5.16 HEALTHCHECK: 健康检查

检查容器的健康性

HEALTHCHECK [选项] CMD <命令>  #设置检查容器健康状况的命令,如果命令执行失败,则返回1,即unhealthy
HEALTHCHECK NONE    #如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令

HEALTHCHECK 支持下列选项:  
--interval=<间隔>    #两次健康检查的间隔,默认为 30 秒
--timeout=<时长>     #健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒
--retries=<次数>     #当连续失败指定次数后,则将容器状态视为 unhealthy,默认3次
--start-period=<FDURATION> #default: 0s

#检查结果返回值:
0  #success    the container is healthy and ready for use
1  #unhealthy  the container is not working correctly
2  #reserved   do not use this exit code

范例:

FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s CMD curl -fs http://localhost/ 

范例:

#如果健康性检查成功,STATUS会显示 (healthy) 
[root@centos8 nginx-1.18]#docker ps 
CONTAINER ID       IMAGE                     COMMAND                 CREATED   
          STATUS                   PORTS                         NAMES
fad976015cad       nginx-centos7.9:v1.18-11   "/apps/nginx/sbin/ng…"   6
seconds ago       Up 5 seconds (healthy)   0.0.0.0:80->80/tcp, 443/tcp   n1

#如果健康性检查不通过,STATUS会显示(unhealthy) 
[root@centos8 nginx-1.18]#docker ps 
CONTAINER ID       IMAGE                     COMMAND                 CREATED   
          STATUS                         PORTS                         NAMES
fad976015cad       nginx-centos7.9:v1.18-11   "/apps/nginx/sbin/ng…"   About a 
minute ago   Up About a minute (unhealthy)   0.0.0.0:80->80/tcp, 443/tcp   n1

#查看健康状态
[root@centos8 ~]#docker inspect -f '{{.State.Health.Status}}' n1
unhealthy

#以json格式显示
[root@centos8 ~]#docker inspect -f '{{json .State.Health}}' n1
{"Status":"unhealthy","FailingStreak":120,"Log":[{"Start":"2021-01-12T23:26:55.897317224+08:00","End":"2021-01-12T23:26:56.117988749+08:00","ExitCode":22,"Output":""},{"Start":"2021-01-
12T23:27:01.121614252+08:00","End":"2021-01-12T23:27:01.358263627+08:00","ExitCode":22,"Output":""},{"Start":"2021-01-12T23:27:06.362034898+08:00","End":"2021-01-12T23:27:06.596203379+08:00","ExitCode":22,"Output":""},{"Start":"2021-01-
12T23:27:11.597954624+08:00","End":"2021-01-12T23:27:11.857543947+08:00","ExitCode":22,"Output":""},{"Start":"2021-01-12T23:27:16.861453605+08:00","End":"2021-01-12T23:27:17.115885101+08:00","ExitCode":22,"Output":""}]}

[root@centos8 ~]#docker inspect -f '{{json .State.Health}}' n1 | python3 -m 
json.tool
{
    "Status": "unhealthy",
    "FailingStreak": 136,
    "Log": [
       {
            "Start": "2021-01-12T23:28:19.950974202+08:00",
            "End": "2021-01-12T23:28:20.204912349+08:00",
            "ExitCode": 22,
            "Output": ""
       },
       {
            "Start": "2021-01-12T23:28:25.208822104+08:00",
            "End": "2021-01-12T23:28:25.491441522+08:00",
            "ExitCode": 22,
            "Output": ""
       },
       {
            "Start": "2021-01-12T23:28:30.495419338+08:00",
            "End": "2021-01-12T23:28:30.714133496+08:00",
            "ExitCode": 22,
            "Output": ""
       },
       {
            "Start": "2021-01-12T23:28:35.718450539+08:00",
            "End": "2021-01-12T23:28:35.988073766+08:00",
            "ExitCode": 22,
            "Output": ""
       },
       {
            "Start": "2021-01-12T23:28:40.993098183+08:00",
            "End": "2021-01-12T23:28:41.260442962+08:00",
            "ExitCode": 22,
            "Output": ""
       }
   ]
}
2.3.1.5.17 .dockerignore文件

官方文档: https://docs.docker.com/engine/reference/builder/#dockerignore-file

与.gitignore文件类似,生成构建上下文时Docker客户端应忽略的文件和文件夹指定模式

.dockerignore 使用 Go 的文件路径规则 filepath.Match

参考链接: https://golang.org/pkg/path/filepath/#Match

完整的语法

#      #以#开头的行为注释
*      #匹配任何非分隔符字符序列
?      #匹配任何单个非分隔符
\\     #表示 \
**     #匹配任意数量的目录(包括零)例如,**/*.go将排除在所有目录中以.go结尾的所有文件,包括构建上下文的根。
!      #表示取反,可用于排除例外情况

范例:

#排除 test 目录下的所有文件
test/*

#排除 md 目录下的 xttblog.md 文件
md/xttblog.md

#排除 xttblog 目录下的所有 .md 的文件
xttblog/*.md

#排除以 xttblog 为前缀的文件和文件夹
xttblog?

#排除所有目录下的 .sql 文件夹
**/*.sql 

范例:

#除了README的md不排外,排除所有md文件,但不排除README-secret.md
*.md
!README*.md
README-secret.md

#除了所有README的md文件以外的md都排除
*.md
README-secret.md
!README*.md
2.3.1.5.18 Dockerfile 构建过程和指令总结

Dockerfile 构建过程

  • 从基础镜像运行一个容器
  • 执行一条指令,对容器做出修改
  • 执行类似docker commit的操作,提交一个新的中间镜像层(可以利用中间层镜像创建容器进行调试和排错)
  • 再基于刚提交的镜像运行一个新容器
  • 执行Dockerfile中的下一条指令,直至所有指令执行完毕

Dockerfile 指令总结

image-20240216144747390

2.3.1.6 构建镜像docker build 命令

docker build命令使用Dockerfile文件创建镜像

docker build [OPTIONS] PATH | URL | -

说明:  
PATH | URL | -     #可以使是本地路径,也可以是URL路径。若设置为 - ,则从标准输入获取
Dockerfile的内容
-f, --file string  #Dockerfile文件名,默认为 PATH/Dockerfile
--force-rm      #总是删除中间层容器,创建镜像失败时,删除临时容器
--no-cache      #不使用之前构建中创建的缓存
-q  --quiet=false  #不显示Dockerfile的RUN运行的输出结果
--rm=true       #创建镜像成功时,删除临时容器
-t --tag list   #设置注册名称、镜像名称、标签。格式为 <注册名称>/<镜像名称>:<标签>(标签默认为latest)

范例:

docker build .
docker build /usr/local/src/nginx
docker build -f /path/to/a/Dockerfile .
docker build -t shykes/myapp .
docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .
docker build -t test/myapp .
docker build -t nginx:v1 /usr/local/src/nginx

查看镜像的构建历史:

docker history 镜像ID

范例:

[root@centos8 ~]#podman history 90201858b1fc
ID             CREATED         CREATED BY                                     
SIZE     COMMENT
90201858b1fc   17 minutes ago   /bin/sh -c #(nop) CMD ["tail" ,"-f","/etc/...   0B 
<missing>      2 hours ago     /bin/sh -c apt-get update && apt-get insta...    14.83MB   
<missing>      35 hours ago     /bin/sh -c #(nop) CMD ["/bin/bash"] 14.83MB   
<missing>      35 hours ago     /bin/sh -c mkdir -p /run/systemd && echo '...   3.072kB   
<missing>      35 hours ago     /bin/sh -c set -xe && echo '#!/bin/sh' > /...   15.87kB   
<missing>      35 hours ago     /bin/sh -c [ -z "$(apt-get indextargets)" ]     991.2kB   
<missing>      35 hours ago     /bin/sh -c #(nop) ADD file:91a750fb184711f...   65.58MB 

范例: 利用Dockerfile构建基于CentOS的nginx镜像

[root@ubuntu1804 ~]#vim /data/Dockerfile 
[root@ubuntu1804 ~]#cat /data/Dockerfile 
FROM centos 
LABEL maintainer="zhou <root@zhou.com>"
RUN yum install -y nginx && echo Nginx Website in Docker > /usr/share/nginx/html/index.html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
#ENTRYPOINT ["nginx", "-g", "daemon off;"]

[root@ubuntu1804 ~]#docker build -t nginx_centos8.2:v1.14.1 /data/
Sending build context to Docker daemon  209.2MB
Step 1/6 : FROM centos
---> 831691599b88
Step 2/6 : LABEL maintainer="zhou <root@zhou.com>"
---> Running in 3fc4487e80f9
Removing intermediate container 3fc4487e80f9
---> 598318841b8a
Step 3/6 : RUN yum install -y nginx
---> Running in 8a6d9866e4ae
CentOS-8 - AppStream                            2.1 MB/s | 5.8 MB     00:02    
CentOS-8 - Base                                 1.8 MB/s | 2.2 MB     00:01    
CentOS-8 - Extras                               8.6 kB/s | 7.0 kB     00:00   
.......
Complete!
Removing intermediate container 8a6d9866e4ae
---> 8963fb608c33
Step 4/6 : RUN echo Nginx Website in Docker > /usr/share/nginx/html/index.html
---> Running in 04d4287aac49
Removing intermediate container 04d4287aac49
---> 9a95e56b9bc0
Step 5/6 : EXPOSE 80
---> Running in 8534523d8aa6
Removing intermediate container 8534523d8aa6
---> 23cca5737903
Step 6/6 : CMD ["nginx", "-g", "daemon off;"]
---> Running in d52fcc21444f
Removing intermediate container d52fcc21444f
---> afdaec99eb35
Successfully built afdaec99eb35
Successfully tagged nginx_centos8.2:v1.14.1

[root@ubuntu1804 ~]#docker run -d -P --name nginx-web nginx_centos8.2:v1.14.1 
3faba5a49f63ab3a1b031da5e4940303b10ddf349069184597e703c572d257d8

[root@ubuntu1804 ~]#docker ps
CONTAINER ID       IMAGE                     COMMAND                 CREATED   
          STATUS             PORTS                   NAMES
3faba5a49f63       nginx_centos8.2:v1.14.1   "nginx -g 'daemon of…"   4 seconds 
ago       Up 3 seconds        0.0.0.0:32775->80/tcp   nginx-web

[root@ubuntu1804 ~]#curl http://127.0.0.1/32775
curl: (7) Failed to connect to 127.0.0.1 port 80: Connection refused

[root@ubuntu1804 ~]#curl http://127.0.0.1:32775
Nginx Website in Docker

[root@ubuntu1804 ~]#curl -I http://127.0.0.1:32775
HTTP/1.1 200 OK
Server: nginx/1.14.1
Date: Wed, 22 Jul 2020 17:09:15 GMT
Content-Type: text/html
Content-Length: 24
Last-Modified: Wed, 22 Jul 2020 17:04:40 GMT
Connection: keep-alive
ETag: "5f1871a8-18"
Accept-Ranges: bytes

范例: 刷新镜像缓存重新构建新镜像

[root@ubuntu1804 ~]#cat /data/Dockerfile
FROM centos 
LABEL maintainer="zhou <root@zhou.com>"
RUN yum install -y nginx
RUN echo Nginx Website in Docker > /usr/share/nginx/html/index.html
#修改下面行,从下面行开始不再使用缓存
ENV REFRESH_DATA 2020-01-01
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

[root@ubuntu1804 ~]#docker build -t nginx_centos8.2:v1.14.1 /data/
Sending build context to Docker daemon  209.2MB
Step 1/7 : FROM centos
---> 831691599b88
Step 2/7 : LABEL maintainer="zhou <root@zhou.com>"
---> Using cache
---> 598318841b8a
Step 3/7 : RUN yum install -y nginx
---> Using cache
---> 8963fb608c33
Step 4/7 : RUN echo Nginx Website in Docker > /usr/share/nginx/html/index.html
---> Using cache
---> 9a95e56b9bc0
Step 5/7 : ENV REFRESH_DATA 2020-01-01  #从此行开始不再利用缓存
---> Running in 4607ee0d0e77
Removing intermediate container 4607ee0d0e77
---> d6235889f336
Step 6/7 : EXPOSE 80
---> Running in 6924aab5c5c8
Removing intermediate container 6924aab5c5c8
---> 545393760683
Step 7/7 : CMD ["nginx", "-g", "daemon off;"]
---> Running in 345bbc6179d8
Removing intermediate container 345bbc6179d8
---> 4bafc2d0c7e0
Successfully built 4bafc2d0c7e0
Successfully tagged nginx_centos8.2:v1.14.1

#全部不利用缓存重新构建镜像
[root@ubuntu1804 ~]#docker build --no-cache -t nginx_centos8.2:v1.14.1 /data/
Sending build context to Docker daemon  209.2MB
Step 1/7 : FROM centos
---> 831691599b88
Step 2/7 : LABEL maintainer="zhou <root@zhou.com>"
---> Running in 41f2aab6657f
Removing intermediate container 41f2aab6657f
---> 091969d0ed9e
Step 3/7 : RUN yum install -y nginx
---> Running in 6e174d492348
CentOS-8 - AppStream                            4.2 MB/s | 5.8 MB     00:01    
CentOS-8 - Base                                 1.7 MB/s | 2.2 MB     00:01    
CentOS-8 - Extras                               1.2 kB/s | 7.0 kB     00:05
Dependencies resolved.
......                               
Complete!
Removing intermediate container 6e174d492348
---> ba62ac34b951
Step 4/7 : RUN echo Nginx Website in Docker > /usr/share/nginx/html/index.html
---> Running in d6f785b28ef6
Removing intermediate container d6f785b28ef6
---> 6e15fdc84e21
Step 5/7 : ENV REFRESH_DATA 2020-06-06
---> Running in c6fd87ed95f6
Removing intermediate container c6fd87ed95f6
---> 328b8621ec36
Step 6/7 : EXPOSE 80
---> Running in 1af3d6964d81
Removing intermediate container 1af3d6964d81
---> 7c513643b182
Step 7/7 : CMD ["nginx", "-g", "daemon off;"]
---> Running in fd9216490941
Removing intermediate container fd9216490941
---> 0b2b61dd0445
Successfully built 0b2b61dd0445
Successfully tagged nginx_centos8.2:v1.14.1

2.3.2 实战案例: 从零开始写一个 Dockerfile

# 将 docker.io-centos.tar 上传到 Linux ,手动解压
[root@docker ~]# docker load -i docker.io-centos.tar
d1be66a59bc5: Loading layer  212.1MB/212.1MB
db6721074924: Loading layer  1.783MB/1.783MB
Loaded image: centos:latest

[root@docker ~]# mkdir dockerfile
[root@docker ~]# cd dockerfile/
[root@docker dockerfile]# vim Dockerfile
FROM centos
MAINTAINER zhouhao
RUN yum install wget -y
RUN yum install epel-release -y
RUN yum install nginx -y
COPY index.html /usr/share/nginx/html/
EXPOSE 80
ENTRYPOINT ["/usr/sbin/nginx","-g","daemon off;"]

[root@docker dockerfile]# vim index.html
<html>
<head>
	<title>page added to dockerfile</title>
</head>
<body>
	<h1>welcome to china, I am zhou </h1>
</body>
</html>

# 基于上面的 dockerfile 构建镜像
[root@docker dockerfile]# docker build -t="zhou/nginx:v1" .

# 查看镜像是否创建成功
[root@docker dockerfile]# docker images | grep nginx
zhou/nginx   v1        0df279ac246e   56 seconds ago   696MB

# 基于刚才构建的镜像启动容器
[root@docker ~]# docker run -d -p 80 --name html1 zhou/nginx:v1
c5f4c9c21af0318ac1c03b11f4b8524e26cd80a4aa1452d8572b8f0f28fd2d3e
# 注:--name html1 给容器起个名字,-p 80 容器端口,物理机端口会随机映射

# 查看容器详细信息和端口映射
[root@docker ~]# docker ps | grep html
c5f4c9c21af0   zhouhao/nginx:v1   "/usr/sbin/nginx -g …"   4 minutes ago   Up 4 minutes   0.0.0.0:49153->80/tcp   html1

[root@docker ~]# curl 192.168.2.18:49153
<html>
<head>
	<title>page added to dockerfile</title>
</head>
<body>
	<h1>welcome to china, I am zhou </h1>
</body>
</html>

2.3.3 实战案例: Dockerfile 制作基于基础镜像的Base镜像

2.3.3.1 准备目录结构,下载镜像并初始化系统

#按照业务类型或系统类型等方式划分创建目录环境,方便后期镜像比较多的时候进行分类
[root@ubuntu1804 ~]#mkdir /data/dockerfile/{web/{nginx,apache,tomcat,jdk},system/{centos,ubuntu,alpine,debian}} -p
[root@ubuntu1804 ~]#tree /data/dockerfile/
/data/dockerfile/
├── system
│   ├── alpine
│   ├── centos
│   ├── debian
│   └── ubuntu
└── web
   ├── apache
   ├── jdk
   ├── nginx
   └── tomcat

2.3.3.2 先制作基于基础镜像的系统Base镜像

#先制作基于基础镜像的系统base镜像
[root@ubuntu1804 ~]#cd /data/dockerfile/system/centos/

#创建Dockerfile,注意可以是dockerfile,但无语法着色功能
[root@ubuntu1804 centos]#vim Dockerfile
[root@ubuntu1804 centos]#cat Dockerfile
FROM centos:centos7.7.1908
LABEL maintainer="testuser <root@testuser.com>"
FROM centos:centos7.7.1908
LABEL maintainer="testuser <root@testuser.com>"
RUN yum -y install wget && rm -f /etc/yum.repos.d/* && wget -O /etc/yum.repos.d/CentOS-Base.repo https://repo.huaweicloud.com/repository/conf/CentOS-7-reg.repo && yum -y install vim-enhanced tcpdump lrzsz tree telnet bash-completion net-tools wget curl bzip2 lsof zip unzip nfs-utils gcc make gcc-c++ glibc glibc-devel pcre pcre-devel openssl openssl-devel systemd-devel zlib-devel && yum clean all && rm -f /etc/localtime && ln -s ../usr/share/zoneinfo/Asia/Shanghai /etc/localtime

[root@ubuntu1804 centos]#vim build.sh
[root@ubuntu1804 centos]#cat build.sh
#!/bin/bash
#
docker build -t centos7-base:v1 .

[root@ubuntu1804 centos]#chmod +x build.sh

[root@ubuntu1804 centos]#./build.sh

[root@ubuntu1804 centos]#docker images 
REPOSITORY         TAG                 IMAGE ID           CREATED             SIZE
centos7-base       v1                 1ba1317e06dc        23 seconds ago     402MB
centos             centos7.7.1908     08d05d1d5859        2 months ago       204MB

[root@ubuntu1804 centos]#docker image history centos7-base:v1

[root@centos7 /data/dockerfile/system/centos]#docker image history centos7-base:v1
IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
64f31893cdc9   41 seconds ago   /bin/sh -c yum -y install wget && rm -f /etc…   231MB   
cfba9f0d3c7b   2 minutes ago    /bin/sh -c #(nop)  LABEL maintainer=testuser…   0B    
08d05d1d5859   2 years ago      /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B    
<missing>      2 years ago      /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B    
<missing>      2 years ago      /bin/sh -c #(nop) ADD file:3e2a127b44ed01afc…   204MB

2.3.4 实战案例: Dockerfile 制作基于Base镜像的 nginx 镜像

2.3.4.1 在Dockerfile目录下准备编译安装的相关文件

[root@ubuntu1804 ~]#mkdir /data/dockerfile/web/nginx/1.16
[root@ubuntu1804 ~]#cd /data/dockerfile/web/nginx/1.16
[root@ubuntu1804 1.16]#wget http://nginx.org/download/nginx-1.16.1.tar.gz
[root@ubuntu1804 1.16]#mkdir app/
[root@ubuntu1804 1.16]#echo "Test Page in app" > app/index.html
[root@ubuntu1804 1.16]#tar zcf app.tar.gz app
[root@ubuntu1804 1.16]#ls
app app.tar.gz nginx-1.16.1.tar.gz

2.3.4.2 在一台测试机进行编译安装同一版本的nginx 生成模版配置文件

[root@centos7 ~]#yum -y install vim-enhanced tcpdump lrzsz tree telnet bashcompletion net-tools wget bzip2 lsof tmux man-pages zip unzip nfs-utils gcc make gcc-c++ glibc glibc-devel pcre pcre-devel openssl openssl-devel systemd-devel zlib-devel
[root@centos7 ~]#wget -P /usr/local/src http://nginx.org/download/nginx-1.16.1.tar.gz
[root@centos7 ~]#cd /usr/local/src/
[root@centos7 src]#tar xvf nginx-1.16.1.tar.gz 
[root@centos7 src]#cd nginx-1.16.1/
[root@centos7 nginx-1.16.1]#./configure --prefix=/apps/nginx && make && make install

#将配置文件复制到nginx镜像的服务器相应目录下
[root@centos7 ~]#scp /apps/nginx/conf/nginx.conf 192.168.1.10:/data/dockerfile/web/nginx/1.16

#准备配置文件
[root@ubuntu1804 1.16]#vim /data/dockerfile/web/nginx/1.16/nginx.conf
worker_processes  1;
user nginx;   
daemon off;   #增加此行,前台运行nginx

2.3.4.3 编写Dockerfile文件

[root@ubuntu1804 ~]#cd /data/dockerfile/web/nginx/1.16

[root@ubuntu1804 nginx]#vim Dockerfile

[root@ubuntu1804 nginx]#cat Dockerfile 
FROM centos7-base:v1
LABEL maintainer="testuser <root@testuser.com>"
ADD nginx-1.16.1.tar.gz /usr/local/src
RUN cd /usr/local/src/nginx-1.16.1 && ./configure --prefix=/apps/nginx && make && make install && rm -rf /usr/local/src/nginx* && rm -rf /usr/local/src/nginx* && useradd -r nginx
COPY nginx.conf /apps/nginx/conf/
ADD app.tar.gz /apps/nginx/html/
EXPOSE 80 443
CMD ["/apps/nginx/sbin/nginx"]

[root@ubuntu1804 nginx]#

2.3.4.4 生成nginx镜像

[root@ubuntu1804 ~]#cd /data/dockerfile/web/nginx/1.16
[root@ubuntu1804 1.16]#ls
app app.tar.gz build.sh Dockerfile nginx-1.16.1.tar.gz nginx.conf
[root@ubuntu1804 1.16]#vim build.sh
[root@ubuntu1804 1.16]#cat build.sh
#!/bin/bash
#
docker build -t nginx-centos7:1.6.1 . 

[root@ubuntu1804 1.16]#chmod +x build.sh
[root@ubuntu1804 1.16]#./build.sh 
[root@ubuntu1804 1.16]##docker images 
REPOSITORY         TAG                 IMAGE ID           CREATED             
SIZE
nginx-centos7       1.6.1            73e4b4b95bca        10 minutes ago      412MB
centos7-base       v1                 1ba1317e06dc       About an hour ago   402MB
centos             centos7.7.1908     08d05d1d5859        2 months ago       204MB

2.3.4.5 生成的容器测试镜像

[root@ubuntu1804 ~]#docker run -d -p 80:80 nginx-centos7:1.6.1
e8e733c6dc96bfb212a15dec04cfcfcac72daf400f5d2423c707aeb778a1859d

[root@ubuntu1804 ~]#docker ps 
CONTAINER ID       IMAGE                 COMMAND                 CREATED       
      STATUS             PORTS                         NAMES
e8e733c6dc96       centos7-nginx:1.6.1   "/apps/nginx/sbin/ng…"   4 seconds ago 
      Up 2 seconds        0.0.0.0:80->80/tcp, 443/tcp   cool_germain
      
[root@ubuntu1804 ~]#docker exec -it e8e733c6dc96 bash
[root@e8e733c6dc96 /]# ps aux
USER       PID %CPU %MEM   VSZ   RSS TTY     STAT START   TIME COMMAND
root          1  0.2  0.2  20572  2468 ?       Ss   03:36   0:00 nginx: master 
process /apps/nginx/sbin/nginx
nginx        12  0.0  0.2  21024  2344 ?       S    03:36   0:00 nginx: worker 
process
root         13  4.0  0.3  12364  3536 pts/0   Ss   03:37   0:00 bash
root         32  0.0  0.3  51764  3460 pts/0   R+   03:37   0:00 ps aux
[root@e8e733c6dc96 /]# exit
exit
[root@ubuntu1804 ~]#curl 127.0.0.1/app/
Test Page in app

2.3.5 实战案例: Dockerfile 直接制作 nginx 镜像

2.3.5.1 在Dockerfile目录下准备编译安装的相关文件

[root@ubuntu1804 ~]#mkdir /data/dockerfile/web/nginx/1.16.1
[root@ubuntu1804 ~]#cd /data/dockerfile/web/nginx/1.16.1
[root@ubuntu1804 1.16.1]#vim nginx.conf
user nginx;
worker_processes  1;
#daemon off;
[root@ubuntu1804 1.16.1]#wget http://nginx.org/download/nginx-1.16.1.tar.gz

2.3.5.2 编写Dockerfile文件

[root@ubuntu1804 1.16.1]#pwd
/data/dockerfile/web/nginx/1.16.1
[root@ubuntu1804 1.16.1]#vim Dockerfile
[root@ubuntu1804 1.16.1]#cat Dockerfile
#Nginx Dockerfile
FROM centos:centos7.7.1908 
MAINTAINER testuser <root@testuser.com>
RUN yum install -y  gcc gcc-c++ pcre pcre-devel zlib zlib-devel openssl openssl-devel \
&& useradd -r -s /sbin/nologin nginx \
&& yum clean all 
ADD nginx-1.16.1.tar.gz /usr/local/src/ 
RUN cd /usr/local/src/nginx-1.16.1 \
&& ./configure --prefix=/apps/nginx \
&& make \
&& make install \
&& rm -rf /usr/local/src/nginx*
ADD nginx.conf /apps/nginx/conf/nginx.conf
COPY index.html /apps/nginx/html/
RUN ln -s /apps/nginx/sbin/nginx /usr/sbin/nginx 
EXPOSE 80 443
CMD ["nginx","-g","daemon off;"]

2.3.5.3 生成nginx镜像

[root@ubuntu1804 ~]#cd /data/dockerfile/web/nginx/1.16.1
[root@ubuntu1804 1.16.1]#vim build.sh
[root@ubuntu1804 1.16.1]#cat build.sh
#!/bin/bash
#
docker build -t nginx-centos7:1.6.1-v2 .

[root@ubuntu1804 1.16.1]#chmod +x build.sh
[root@ubuntu1804 1.16.1]#ls
build.sh Dockerfile index.html nginx-1.16.1.tar.gz nginx.conf
[root@ubuntu1804 1.16.1]#./build.sh 
[root@ubuntu1804 1.16.1]#docker images 
REPOSITORY         TAG                 IMAGE ID           CREATED             SIZE
nginx-centos7       1.6.1-v2           1918d29d5f45        17 minutes ago     328MB
nginx-centos7       1.6.1               8c16774437a5        13 hours ago       412MB
centos7-base       v1                 1ba1317e06dc        15 hours ago       402MB
centos             centos7.7.1908     08d05d1d5859        2 months ago       204MB

2.3.5.4 生成容器测试镜像

[root@ubuntu1804 ~]#docker run -d -p 80:80 nginx-centos7:1.6.1-v2 
21c954ad4fb902076832cc9a52dd1502aca43d9bcd2b46a2f164382e4ac7b3f6

[root@ubuntu1804 ~]#docker ps 
CONTAINER ID       IMAGE                   COMMAND                 CREATED   
          STATUS             PORTS                         NAMES
21c954ad4fb9       centos7-nginx:1.6.1-v2   "nginx -g 'daemon of…"   6 seconds 
ago       Up 4 seconds        0.0.0.0:80->80/tcp, 443/tcp   inspiring_goldwasser

[root@ubuntu1804 ~]#curl 127.0.0.1
Test Page v2 in Docker

[root@ubuntu1804 ~]#docker exec -it 21c954ad4fb9 bash

[root@21c954ad4fb9 /]# ps aux
USER       PID %CPU %MEM   VSZ   RSS TTY     STAT START   TIME COMMAND
root          1  0.5  0.2  20572  2372 ?       Ss   03:30   0:00 nginx: master process nginx -g daemon off;
nginx         6  0.0  0.2  21024  2316 ?       S    03:30   0:00 nginx: worker process
root          7 11.5  0.2  11840  2880 pts/0   Ss   03:31   0:00 bash
root         20  0.0  0.3  51764  3376 pts/0   R+   03:31   0:00 ps aux
[root@21c954ad4fb9 /]# exit
exit

2.3.6 生产案例: 制作自定义tomcat业务镜像

基于官方提供的centos、debian、ubuntu、alpine等基础 镜像构建 JDK (Java环境),然后再基于自定义的 JDK 镜像构建出业务需要的tomcat 镜像.

2.3.6.1 自定义 Centos 系统基础镜像

先基于官方提供的基础镜像,制作出安装了常用命令的自定义基础镜像

[root@ubuntu1804 ~]#docker pull centos:centos7.7.1908
[root@ubuntu1804 ~]#mkdir -p 
/data/dockerfile/{web/{nginx,tomcat,jdk},system/{centos,ubuntu,alpine,debian}} 

[root@ubuntu1804 ~]#cd /data/dockerfile/system/centos/ 

[root@ubuntu1804 centos]#vim Dockerfile

[root@ubuntu1804 centos]#cat Dockerfile  
# Centos Base Image 
FROM centos:centos7.7.1908
LABEL maintainer="testuser <root@testuser.com>"
RUN yum -y install wget && rm -f /etc/yum.repos.d/* && wget -P /etc/yum.repos.d/ 
http://mirrors.aliyun.com/repo/Centos-7.repo \
   && wget -P /etc/yum.repos.d/ http://mirrors.aliyun.com/repo/epel-7.repo \
   && yum -y install vim-enhanced tcpdump lrzsz tree telnet bash-completion net-tools wget bzip2 lsof zip unzip nfs-utils gcc make gcc-c++ glibc glibcdevel pcre pcre-devel openssl openssl-devel systemd-devel zlib-devel \
   && yum clean all \
   && rm -f /etc/localtime \
   && ln -s ../usr/share/zoneinfo/Asia/Shanghai /etc/localtime
   
#添加系统账户
RUN groupadd www -g 2019 && useradd www -u 2019 -g www 
[root@ubuntu1804 centos]#vim build.sh
#通过脚本构建镜像
[root@ubuntu1804 centos]#cat build.sh  
#!/bin/bash 
docker build -t centos7-base:v1 .

[root@ubuntu1804 centos]#bash build.sh 
[root@ubuntu1804 centos]#docker images 
REPOSITORY         TAG                 IMAGE ID           CREATED             
SIZE
centos7-base       v1                 34ab3afcd3b3        4 seconds ago       403MB
centos             centos7.7.1908     08d05d1d5859        2 months ago       204MB

2.3.6.2 构建 JDK 镜像

上传JDK压缩包和profile文件上传到Dockerfile当前目录

#将CentOS7主机上的/etc/profile文件传到 Dockerfile 所在目录下
[root@ubuntu1804 ~]#scp centos7:/etc/profile 10.0.0.100:/data/dockerfile/web/jdk

#修改profile文件,加下面四行相关变量
[root@ubuntu1804 ~]#vim /data/dockerfile/web/jdk/profile
[root@ubuntu1804 ~]#tail -n 4 /data/dockerfile/web/jdk/profile
export JAVA_HOME=/usr/local/jdk
export TOMCAT_HOME=/apps/tomcat
export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$TOMCAT_HOME/bin:$PATH
export CLASSPATH=.$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$JAVA_HOME/lib/tools.jar

#下载jdk文件传到Dockfile目录下
#https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads2133151.html
[root@ubuntu1804 ~]#tree /data/dockerfile/web/jdk
/data/dockerfile/web/jdk
├── jdk-8u212-linux-x64.tar.gz
└── profile
0 directories, 2 files

2.3.6.3 准备Dockerfile文件

[root@ubuntu1804 ~]#vim /data/dockerfile/web/jdk/Dockerfile 
[root@ubuntu1804 ~]#cat /data/dockerfile/web/jdk/Dockerfile
#JDK Base Image
FROM centos7-base:v1
LABEL maintainer="testuser <root@testuser.com>"
ADD jdk-8u212-linux-x64.tar.gz /usr/local/src/
RUN ln -s /usr/local/src/jdk1.8.0_212 /usr/local/jdk
ADD profile /etc/profile
ENV JAVA_HOME /usr/local/jdk
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/:$JRE_HOME/lib/
ENV PATH $PATH:$JAVA_HOME/bin

2.3.6.4 执行构建脚本制作镜像

[root@ubuntu1804 ~]#vim /data/dockerfile/web/jdk/build.sh

[root@ubuntu1804 ~]#cat /data/dockerfile/web/jdk/build.sh
#!/bin/bash
docker build -t centos7-jdk:8u212 .

[root@ubuntu1804 ~]#tree /data/dockerfile/web/jdk/
/data/dockerfile/web/jdk/
├── build.sh
├── Dockerfile
├── jdk-8u212-linux-x64.tar.gz
└── profile
0 directories, 4 files

[root@ubuntu1804 ~]#cd /data/dockerfile/web/jdk/

[root@ubuntu1804 jdk]#bash build.sh

[root@ubuntu1804 jdk]#docker images 
REPOSITORY         TAG                 IMAGE ID           CREATED            SIZE
centos7-jdk         8u212               fdbeb8a49ea6      59 seconds ago     809MB
centos7-base       v1                 34ab3afcd3b3        44 minutes ago     403MB
centos             centos7.7.1908     08d05d1d5859        2 months ago       204MB

2.3.6.5 从镜像启动容器测试

[root@ubuntu1804 jdk]#docker run -it --rm centos7-jdk:8u212 bash
[root@25c9c0266bd2 /]# java -version
java version "1.8.0_212"
Java(TM) SE Runtime Environment (build 1.8.0_212-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.212-b10, mixed mode)

2.3.7 从JDK镜像构建tomcat 8 Base镜像

基于自定义的 JDK 基础镜像,构建出通用的自定义 Tomcat 基础镜像,此镜像后期会被多个业务的多个服务共同引用(相同的JDK 版本和Tomcat 版本).

2.3.7.1 上传tomcat 压缩包

[root@ubuntu1804 ~]#mkdir -p /data/dockerfile/web/tomcat/tomcat-base-8.5.50
[root@ubuntu1804 ~]#cd /data/dockerfile/web/tomcat/tomcat-base-8.5.50
[root@ubuntu1804 tomcat-base-8.5.50]#wget http://archive.apache.org/dist/tomcat/tomcat-8/v8.5.50/bin/apache-tomcat-8.5.50.tar.gz

2.3.7.2 编辑Dockerfile

[root@ubuntu1804 ~]#cat /data/dockerfile/web/tomcat/tomcat-base8.5.50/Dockerfile 
#Tomcat Base Image 
FROM centos7-jdk:8u212 
LABEL maintainer="testuser <root@testuser.com>"
#env 
ENV TZ "Asia/Shanghai"
ENV LANG en_US.UTF-8 
ENV TERM xterm 
ENV TOMCAT_MAJOR_VERSION 8
ENV TOMCAT_MINOR_VERSION 8.5.50 
ENV CATALINA_HOME /apps/tomcat 
ENV APP_DIR ${CATALINA_HOME}/webapps 
RUN mkdir /apps  
ADD apache-tomcat-8.5.50.tar.gz /apps   
RUN ln -s /apps/apache-tomcat-8.5.50 /apps/tomcat

2.3.7.3 通过脚本构建tomcat 基础镜像

[root@ubuntu1804 tomcat-base-8.5.50]#vim build.sh 
[root@ubuntu1804 tomcat-base-8.5.50]#cat build.sh 
#!/bin/bash
docker build -t tomcat-base:v8.5.50 .

[root@ubuntu1804 tomcat-base-8.5.50]#tree
.
├── apache-tomcat-8.5.50.tar.gz
├── build.sh
└── Dockerfile
0 directories, 3 files

[root@ubuntu1804 tomcat-base-8.5.50]#bash build.sh

[root@ubuntu1804 tomcat-base-8.5.50]#docker images
REPOSITORY         TAG                 IMAGE ID           CREATED             SIZE
tomcat-base         v8.5.50             8d5395cb72c4        3 seconds ago       824MB
centos7-jdk         8u212               e0fe770a7ccd        22 minutes ago     809MB
centos7-base       v1                 34ab3afcd3b3        2 hours ago         403MB
centos             centos7.7.1908     08d05d1d5859        2 months ago       204MB

2.3.7.4 验证镜像构建完成

[root@ubuntu1804 tomcat-base-8.5.50]#docker run -it --rm -p 8080:8080 tomcat-base:v8.5.50 bash   
[root@d0a387e0ccc9 /]# /apps/tomcat/bin/catalina.sh start
Using CATALINA_BASE:   /apps/tomcat
Using CATALINA_HOME:   /apps/tomcat
Using CATALINA_TMPDIR: /apps/tomcat/temp
Using JRE_HOME:       /usr/local/jdk/jre
Using CLASSPATH:       /apps/tomcat/bin/bootstrap.jar:/apps/tomcat/bin/tomcatjuli.jar
Tomcat started.
[root@d0a387e0ccc9 /]# netstat -ntl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 0.0.0.0:8009            0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN     
[root@d0a387e0ccc9 /]# 

2.3.8 构建业务镜像1

创建tomcat-app1和tomcat-app2两个目录,代表不同的两个基于tomcat的业务。

2.3.8.1 准备tomcat的配置文件

[root@ubuntu1804 ~]#mkdir -p /data/dockerfile/web/tomcat/tomcat-app{1,2}

[root@ubuntu1804 ~]#tree /data/dockerfile/web/tomcat/

#上传和修改server.xml 
[root@ubuntu1804 ~]#cd /data/dockerfile/web/tomcat/tomcat-base-8.5.50

[root@ubuntu1804 tomcat-base-8.5.50]#tar xf apache-tomcat-8.5.50.tar.gz

[root@ubuntu1804 tomcat-base-8.5.50]#cp apache-tomcat-8.5.50/conf/server.xml /data/dockerfile/web/tomcat/tomcat-app1/

[root@ubuntu1804 tomcat-base-8.5.50]#cd /data/dockerfile/web/tomcat/tomcat-app1/

[root@ubuntu1804 tomcat-app1]#vim server.xml
......
 <Host name="localhost"  appBase="/data/tomcat/webapps"                         
    
            unpackWARs="true" autoDeploy="true">

2.3.8.2 准备自定义页面

[root@ubuntu1804 tomcat-app1]#mkdir app
[root@ubuntu1804 tomcat-app1]#echo "Tomcat Page in app1" > app/index.jsp
[root@ubuntu1804 tomcat-app1]#tar zcf app.tar.gz app

2.3.8.3 准备容器启动执行脚本

[root@ubuntu1804 tomcat-app1]#vim run_tomcat.sh
[root@ubuntu1804 tomcat-app1]#cat run_tomcat.sh
#!/bin/bash
echo "nameserver 180.76.76.76" > /etc/resolv.conf

su - www -c "source /etc/profile.d/jdk1.8.0_212 \&& /apps/tomcat/bin/catalina.sh start \
&& tail -f /etc/hosts"



[root@ubuntu1804 tomcat-app1]#chmod a+x run_tomcat.sh 

2.3.8.4 准备Dockerfile

[root@ubuntu1804 tomcat-app1]#vim Dockerfile
#Tomcat Web Image 
FROM tomcat-base:v8.5.50
LABEL maintainer="testuser <root@testuser.com>"
ADD server.xml /apps/tomcat/conf/server.xml
ADD run_tomcat.sh /apps/tomcat/bin/run_tomcat.sh 
ADD app.tar.gz /data/tomcat/webapps/ 
RUN chown -R www.www /apps/ /data/tomcat/   
EXPOSE 8080 8009
CMD ["/apps/tomcat/bin/run_tomcat.sh"]

2.3.8.5 执行构建脚本制作镜像

[root@ubuntu1804 tomcat-app1]#vim build.sh
[root@ubuntu1804 tomcat-app1]#cat build.sh
#!/bin/bash
docker build -t tomcat-web:app1 .

[root@ubuntu1804 tomcat-app1]#pwd
/data/dockerfile/web/tomcat/tomcat-app1

[root@ubuntu1804 tomcat-app1]#tree
[root@ubuntu1804 tomcat-app1]#bash build.sh 
[root@ubuntu1804 tomcat-app1]#docker images

2.3.8.6 从镜像启动测试容器

[root@ubuntu1804 tomcat-app1]#docker run -d -p 8080:8080 tomcat-web:app1
82e6690e36c3a6faf2dae62bd706a89cbba490d567c841c37501f0fba670ea25

2.3.8.7 访问测试

[root@ubuntu1804 ~]#curl 127.0.0.1:8080/app/
Tomcat Page in app1

[root@ubuntu1804 ~]#docker exec -it 82e6690e36c3 bash

[root@82e6690e36c3 /]# ps aux
USER       PID %CPU %MEM   VSZ   RSS TTY     STAT START   TIME COMMAND
root          1  0.0  0.2  15136  2248 ?       Ss   22:14   0:00 /bin/bash 
/apps/tomcat/bin/run_tomcat.sh
www          25  0.8  9.7 2241656 95924 ?       Sl   22:14   0:04

/usr/local/jdk/bin/java -Djava.util.logging.config.file=/apps/tomcat
root         26  0.0  0.4  85428  4472 ?       S    22:14   0:00 su - www -c tail -f /etc/hosts
www          27  0.0  0.0   4416   720 ?       Ss   22:14   0:00 tail -f /etc/hosts
root         82 25.0  0.3  15800  3820 pts/0   Ss   22:22   0:00 bash
root        101  0.0  0.3  55196  3836 pts/0   R+   22:22   0:00 ps aux

[root@82e6690e36c3 /]# vim /data/tomcat/webapps/app/index.jsp 
[root@82e6690e36c3 /]# cat /data/tomcat/webapps/app/index.jsp
Tomcat Page in app1 v2

[root@82e6690e36c3 /]# /apps/tomcat/bin/catalina.sh stop
Using CATALINA_BASE:   /apps/tomcat
Using CATALINA_HOME:   /apps/tomcat
Using CATALINA_TMPDIR: /apps/tomcat/temp
Using JRE_HOME:       /usr/local/jdk/jre
Using CLASSPATH:       /apps/tomcat/bin/bootstrap.jar:/apps/tomcat/bin/tomcatjuli.jar

[root@82e6690e36c3 /]# /apps/tomcat/bin/catalina.sh start
Using CATALINA_BASE:   /apps/tomcat
Using CATALINA_HOME:   /apps/tomcat
Using CATALINA_TMPDIR: /apps/tomcat/temp
Using JRE_HOME:       /usr/local/jdk/jre
Using CLASSPATH:       /apps/tomcat/bin/bootstrap.jar:/apps/tomcat/bin/tomcatjuli.jar
Tomcat started.

[root@ubuntu1804 tomcat-app1]#curl 127.0.0.1:8080/app/
Tomcat Page in app1 v2

2.3.9 构建业务镜像2

准备自定义页面和其它数据

[root@ubuntu1804 tomcat]#pwd
/data/dockerfile/web/tomcat

[root@ubuntu1804 tomcat]#cp -a tomcat-app1/* tomcat-app2/

[root@ubuntu1804 tomcat]#tree tomcat-app2/
tomcat-app2/
├── app
│   └── index.jsp
├── app.tar.gz
├── build.sh
├── Dockerfile
├── run_tomcat.sh
└── server.xml
1 directory, 6 files

[root@ubuntu1804 tomcat]#cd tomcat-app2
[root@ubuntu1804 tomcat-app2]#echo "Tomcat Page in app2" > app/index.html
[root@ubuntu1804 tomcat-app2]#rm -f app.tar.gz 
[root@ubuntu1804 tomcat-app2]#tar zcf app.tar.gz app

2.3.9.1 准备容器启动脚本run_tomcat.sh

和业务1一样不变

2.3.9.2 准备Dockerfile

和业务1一样不变

2.3.9.3 执行构建脚本制作镜像

[root@ubuntu1804 tomcat-app2]#vim build.sh
[root@ubuntu1804 tomcat-app2]#cat build.sh
#!/bin/bash
docker build -t tomcat-web:app2 .

[root@ubuntu1804 tomcat-app2]#bash build.sh
[root@ubuntu1804 tomcat-app2]#docker images 
REPOSITORY         TAG                 IMAGE ID           CREATED             
SIZE
tomcat-web         app2               0e1760fe79a6        37 seconds ago     838MB
tomcat-web         app1               76016219a0ca        27 minutes ago     838MB
tomcat-base         v8.5.50             8d5395cb72c4      2 hours ago        824MB
centos7-jdk         8u212               e0fe770a7ccd      2 hours ago        809MB
centos7-base       v1                 34ab3afcd3b3        3 hours ago        403MB
centos             centos7.7.1908     08d05d1d5859        2 months ago       204MB

2.3.9.4 从镜像启动容器测试

[root@ubuntu1804 tomcat-app2]#docker run -d -p 8082:8080 tomcat-web:app2
3fc9437e42099e92f88e8e09bac0507f2d837ac8a6dba8cb1e4efc934bcf81ff

2.3.9.5 访问测试

[root@ubuntu1804 tomcat-app2]#curl 127.0.0.1:8082/app/
Tomcat Page in app2

2.3.10 生产案例: 构建 haproxy 镜像

2.3.10.1 准备相关文件

#准备haproxy源码文件
[root@ubuntu1804 ~]#mkdir -p /data/dockerfile/web/haproxy/2.1.2-centos7

[root@ubuntu1804 ~]#cd /data/dockerfile/web/haproxy/2.1.2-centos7

[root@ubuntu1804 2.1.2-centos7]#wget https://src.fedoraproject.org/repo/pkgs/haproxy/haproxy-2.1.2.tar.gz/sha512/929d244a67f93337e3c791b1a29f9643ac288acdf0090dc7a05c72caf61c3b04ef376b84737351f0c54d58bca66ed8af18faa20ec3adc97067313732e0dfcd7d/haproxy-2.1.2.tar.gz

#准备haproxy启动脚本
[root@ubuntu1804 2.1.2-centos7]#vim run_haproxy.sh
[root@ubuntu1804 2.1.2-centos7]#cat run_haproxy.sh
#!/bin/bash
haproxy -f /etc/haproxy/haproxy.cfg
tail -f /etc/hosts

[root@ubuntu1804 2.1.2-centos7]#chmod +x run_haproxy.sh

2.3.10.1 准备haproxy配置文件

#准备haproxy配置文件
[root@ubuntu1804 2.1.2-centos7]#cat haproxy.cfg 
global
chroot /apps/haproxy
#stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin
uid 99
gid 99
daemon
nbproc 1
pidfile /apps/haproxy/run/haproxy.pid
log 127.0.0.1 local3 info
defaults
option http-keep-alive
option forwardfor
mode http
timeout connect 300000ms
timeout client 300000ms
timeout server 300000ms
listen stats
 mode http
 bind 0.0.0.0:9999
 stats enable
 log global
 stats uri /haproxy-status
 stats auth haadmin:123456 
listen web_port
 bind 0.0.0.0:80
 mode http
 log global
 balance roundrobin
 server web1 10.0.0.101:8080 check inter 3000 fall 2 rise 5
 server web2 10.0.0.102:8080 check inter 3000 fall 2 rise 5

2.3.10.2 准备Dockerfile

[root@ubuntu1804 2.1.2-centos7]#pwd
/data/dockerfile/web/haproxy/2.1.2-centos7
#Haproxy Base Image
FROM centos7-base:v1
LABEL maintainer="testuser <root@testuser.com>"
ADD haproxy-2.1.2.tar.gz /usr/local/src/ 
RUN cd /usr/local/src/haproxy-2.1.2 \
&& make ARCH=x86_64 TARGET=linux-glibc USE_PCRE=1 USE_OPENSSL=1 USE_ZLIB=1 USE_SYSTEMD=1 USE_CPU_AFFINITY=1 PREFIX=/apps/haproxy \
&& make install PREFIX=/apps/haproxy \
&& ln -s /apps/haproxy/sbin/haproxy /usr/sbin/ \
&& mkdir /apps/haproxy/run \
&& rm -rf /usr/local/src/haproxy*  
ADD haproxy.cfg /etc/haproxy/
ADD run_haproxy.sh /usr/bin
EXPOSE 80 9999
CMD ["run_haproxy.sh"]

2.3.10.3 准备构建脚本构建haproxy镜像

[root@ubuntu1804 2.1.2-centos7]#vim build.sh 
[root@ubuntu1804 2.1.2-centos7]#cat build.sh 
#!/bin/bash
docker build -t haproxy-centos7:2.1.2 .

[root@ubuntu1804 2.1.2-centos7]#ls
build.sh Dockerfile haproxy-2.1.2.tar.gz haproxy.cfg run_haproxy.sh

[root@ubuntu1804 2.1.2-centos7]#bash build.sh 
[root@ubuntu1804 2.1.2-centos7]#docker images 

2.3.10.4 从镜像启动容器

[root@ubuntu1804 2.1.2-centos7]#docker run -d -p 80:80 -p 9999:9999 haproxy-centos7:2.1.2 
e0a7c827cb5fdd5a630f7dfe58b1f60822da18929a4dfeccb7490fb78403e3df

2.3.10.5 在另外两台主机启动容器

#导出本地相关镜像
[root@ubuntu1804 ~]#docker save centos7-base:v1 > /data/centos7-base.tar.gz
[root@ubuntu1804 ~]#docker save centos7-jdk:8u212 > /data/centos7-jdk.tar.gz
[root@ubuntu1804 ~]#docker save tomcat-base:v8.5.50 > /data/tomcat-base.tar.gz
[root@ubuntu1804 ~]#docker save tomcat-web:app1 > /data/tomcat-web-app1.tar.gz
[root@ubuntu1804 ~]#docker save tomcat-web:app2 > /data/tomcat-web-app2.tar.gz

[root@ubuntu1804 ~]#ls /data
centos7-base.tar.gz centos7-jdk.tar.gz dockerfile tomcat-base.tar.gz tomcatweb-app1.tar.gz tomcat-web-app2.tar.gz

#将镜像复制到另外两台主机
[root@ubuntu1804 ~]#scp /data/*.gz 10.0.0.101:/data/
[root@ubuntu1804 ~]#scp /data/*.gz 10.0.0.102:/data/   

#在另外两台主机上执行下面操作导入镜像
[root@ubuntu1804 ~]#ls /data
centos7-base.tar.gz lost+found         tomcat-web-app1.tar.gz
centos7-jdk.tar.gz   tomcat-base.tar.gz tomcat-web-app2.tar.gz
[root@ubuntu1804 ~]#for i in /data/*.gz;do docker load -i $i;done

#在另外两台主机上创建相关容器
[root@ubuntu1804 ~]#docker run -d -p 8080:8080 tomcat-web:app1 
781681e73333396b23f404e70d0c781ab464a8e9b578f41c153583d23bd76a46
[root@ubuntu1804 ~]#docker run -d -p 8080:8080 tomcat-web:app2
81fa01a688cb72cf397a5da46acc89a51f2a2f8de3a0072565d701625c43540a

2.3.10.6 web访问验证

[root@ubuntu1804 2.1.2-centos7]#curl http://10.0.0.100/app/
Tomcat Page in app1

[root@ubuntu1804 2.1.2-centos7]#curl http://10.0.0.100/app/
Tomcat Page in app2

[root@ubuntu1804 2.1.2-centos7]#docker exec -it e0a7c827cb5 bash

[root@e0a7c827cb5f /]# netstat -ntl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 0.0.0.0:9999            0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN  

[root@e0a7c827cb5f /]# vim /etc/haproxy/haproxy.cfg 
[root@e0a7c827cb5f /]# ps aux

image-20240216150704193

image-20240216150708760

#在第二台主机上停止容器
[root@ubuntu1804 ~]#docker stop 81fa01a688cb

[root@ubuntu1804 ~]#docker ps -a
CONTAINER ID       IMAGE               COMMAND                 CREATED         
    STATUS                       PORTS               NAMES
81fa01a688cb       tomcat-web:app2     "/apps/tomcat/bin/ru…"   28 minutes ago 
    Exited (137) 39 seconds ago 
    
#观察状态页,发现后端服务器down

image-20240216150726009

#在第二台主机上恢复启动容器
[root@ubuntu1804 ~]#docker start 81fa01a688cb
81fa01a688cb

[root@ubuntu1804 ~]#docker ps -a
CONTAINER ID       IMAGE               COMMAND                 CREATED         
    STATUS             PORTS                             NAMES
81fa01a688cb       tomcat-web:app2     "/apps/tomcat/bin/ru…"   30 minutes ago 
    Up 14 seconds       8009/tcp, 0.0.0.0:8080->8080/tcp   optimistic_shirley
    
#再次观察状态页,发现后端服务器上线

image-20240216150734995

2.3.11 基于 alpine 基础镜像制作nginx镜像

2.3.11.1 制作alpine的自定义系统镜像

#下载alpine镜像,打新标签
[root@ubuntu1804 ~]#docker pull alpine
[root@ubuntu1804 ~]#docker tag alpine alpine:3.11
[root@ubuntu1804 ~]#docker images
[root@ubuntu1804 ~]#docker images 
REPOSITORY         TAG                 IMAGE ID           CREATED             
SIZE
alpine              3.11               e7d92cdc71fe        11 days ago         
5.59MB
alpine             latest             e7d92cdc71fe        11 days ago         
5.59MB
#准备相关文件
[root@ubuntu1804 ~]#cd /data/dockerfile/system/alpine

[root@ubuntu1804 alpine]#vim repositories 
[root@ubuntu1804 alpine]#cat repositories 
http://mirrors.aliyun.com/alpine/v3.11/main
http://mirrors.aliyun.com/alpine/v3.11/community

#准备Dockerfile文件
[root@ubuntu1804 alpine]#cat Dockerfile 
FROM alpine:3.11 
LABEL maintainer="testuser <root@testuser.com>"
COPY repositories /etc/apk/repositories 
RUN apk update && apk --no-cache add iotop gcc libgcc libc-dev libcurl libc-utils pcre-dev zlib-dev libnfs make pcre pcre2 zip unzip net-tools pstree wget libevent libevent-dev iproute2

#准备构建脚本
[root@ubuntu1804 alpine]#cat build.sh 
#!/bin/bash
docker build -t alpine-base:3.11 .

[root@ubuntu1804 alpine]#bash build.sh
[root@ubuntu1804 alpine]#docker images alp*
REPOSITORY         TAG                 IMAGE ID           CREATED             SIZE
alpine-base         3.11               b162eecf4da9        5 minutes ago       182MB
alpine              3.11               e7d92cdc71fe        11 days ago         5.59MB
alpine             latest             e7d92cdc71fe        11 days ago         5.59MB

2.3.11.2 制作基于alpine自定义镜像的nginx镜像

#准备相关文件
[root@ubuntu1804 ~]#mkdir /data/dockerfile/web/nginx/1.16.1-alpine/
[root@ubuntu1804 ~]#cd /data/dockerfile/web/nginx/1.16.1-alpine/
[root@ubuntu1804 1.16.1-alpine]#wget http://nginx.org/download/nginx1.16.1.tar.gz
[root@ubuntu1804 1.16.1-alpine]#echo Test Page based nginx-alpine > index.html
[root@ubuntu1804 1.16.1-alpine]#cp ../1.16.1-centos7/nginx.conf .
[root@ubuntu1804 1.16.1-alpine]#cat nginx.conf
user root;
worker_processes  1;
daemon off;
...
location / {
           root   /data/nginx/html;
...
#编写Dockerfile文件
[root@ubuntu1804 1.16.1-alpine]#vim Dockerfile 
[root@ubuntu1804 1.16.1-alpine]#cat Dockerfile
FROM alpine-base:3.11
LABEL maintainer="testuser <root@testuser.com>"
ADD nginx-1.16.1.tar.gz /usr/local/src 
RUN cd /usr/local/src/nginx-1.16.1 && ./configure --prefix=/apps/nginx && make && make install && ln -s /apps/nginx/sbin/nginx /usr/bin/
#RUN addgroup nginx && adduser nginx
COPY nginx.conf /apps/nginx/conf/nginx.conf
ADD index.html /data/nginx/html/index.html
#RUN chown -R nginx.nginx /data/nginx/ /apps/nginx/
EXPOSE 80 443
CMD ["nginx"]

#构建镜像
[root@ubuntu1804 1.16.1-alpine]#vim build.sh
[root@ubuntu1804 1.16.1-alpine]#cat build.sh
#!/bin/bash
#********************************************************************
docker build -t nginx-alpine:1.16.1 .

[root@ubuntu1804 1.16.1-alpine]#ls
build.sh Dockerfile index.html nginx-1.16.1.tar.gz nginx.conf

[root@ubuntu1804 1.16.1-alpine]#docker images "*alpine*"

#生成容器测试镜像
[root@ubuntu1804 1.16.1-alpine]#docker run -d -p 80:80 nginx-alpine:1.16.1
1cb16e9fe6cd8e583a61c2718a92ce3031313bbf3656c2f85ac84d34ccfe7e0d

[root@ubuntu1804 1.16.1-alpine]#curl 127.0.0.1
Test Page based nginx-alpine

[root@ubuntu1804 1.16.1-alpine]#docker exec -it 1cb16e9fe6cd sh
/ # ps aux
PID   USER     TIME COMMAND
    1 root      0:00 nginx: master process nginx
    6 nginx     0:00 nginx: worker process
    7 root      0:00 sh
   12 root      0:00 ps aux
/ # ls /data/nginx/html/ -l
total 4
-rw-r--r--    1 nginx   nginx           29 Jan 29 11:08 index.html
/ # exit
[root@ubuntu1804 1.16.1-alpine]#

2.3.12 基于 Ubuntu 基础镜像制作 nginx 镜像

#下载ubuntu1804镜像
[root@ubuntu1804 ~]#docker pull ubuntu:18.04
[root@ubuntu1804 ~]#docker images ubuntu*
REPOSITORY         TAG                 IMAGE ID           CREATED             
SIZE
ubuntu              18.04               ccc6e87d482b        13 days ago         
64.2MB
#准备相关文件
[root@ubuntu1804 ~]#mkdir /data/dockerfile/web/nginx/1.16.1-ubuntu1804

[root@ubuntu1804 ~]#cd /data/dockerfile/web/nginx/1.16.1-ubuntu1804

[root@ubuntu1804 1.16.1-ubuntu1804]#vim sources.list
[root@ubuntu1804 1.16.1-ubuntu1804]#cat sources.list
deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse

[root@ubuntu1804 1.16.1-ubuntu1804]#wget http://nginx.org/download/nginx1.16.1.tar.gz

[root@ubuntu1804 1.16.1-ubuntu1804]#cp ../1.16.1-alpine/nginx.conf .

[root@ubuntu1804 1.16.1-ubuntu1804]#echo Test Page based nginx-ubuntu1804 > index.html

#编写Dockerfile文件
[root@ubuntu1804 1.16.1-ubuntu1804]#vim Dockerfile
[root@ubuntu1804 1.16.1-ubuntu1804]#cat Dockerfile 
FROM ubuntu:18.04 
LABEL maintainer="testuser <root@testuser.com>"
COPY sources.list /etc/apt/sources.list
RUN apt update && apt install -y gcc nfs-common nfs-kernel-server openssh-server lrzsz tree openssl libssl-dev libpcre3 libpcre3-dev zlib1g-dev unzip zip make
ADD nginx-1.16.1.tar.gz /usr/local/src
RUN cd /usr/local/src/nginx-1.16.1 && ./configure --prefix=/apps/nginx && make && make install && ln -s /apps/nginx/sbin/nginx /usr/bin && rm -rf /usr/local/src/nginx-1.16.1*
ADD nginx.conf /apps/nginx/conf/nginx.conf
ADD index.html /data/nginx/html/index.html
RUN groupadd -g 2019 nginx && useradd -g nginx -s /usr/sbin/nologin -u 2019 nginx && chown -R nginx.nginx /apps/nginx /data/nginx 
EXPOSE 80 443
CMD ["nginx"]

#构建镜像
[root@ubuntu1804 1.16.1-ubuntu1804]#vim build.sh
[root@ubuntu1804 1.16.1-ubuntu1804]#cat build.sh
#!/bin/bash
docker build -t nginx-ubuntu1804:1.16.1 .

[root@ubuntu1804 1.16.1-ubuntu1804]#bash build.sh
[root@ubuntu1804 1.16.1-ubuntu1804]#ls
build.sh Dockerfile index.html nginx-1.16.1.tar.gz nginx.conf sources.list

[root@ubuntu1804 1.16.1-ubuntu1804]#docker images "nginx*"
REPOSITORY         TAG                 IMAGE ID           CREATED             SIZE
nginx-ubuntu1804    1.16.1             19efdd23ac87        4 minutes ago       378MB
nginx-alpine        1.16.1             978a43bbb61d        40 minutes ago     211MB
nginx-centos7       1.6.1-v2           1918d29d5f45        17 minutes ago     328MB
nginx-centos7       1.6.1               8c16774437a5        13 hours ago       412MB 

#启动容器测试镜像
[root@ubuntu1804 1.16.1-ubuntu1804]#docker run -d -p 80:80 nginxubuntu1804:1.16.1 
58f8e9a8fd6eebb19bd2b7c27bd8d52a3a4d42637a942e1e9179ec1b2bcc559d

[root@ubuntu1804 1.16.1-ubuntu1804]#curl 127.0.0.1
Test Page based nginx-ubuntu1804