一、什么是dockerfile?
Dockerfile是一个包含用于组合映像的命令的文本文档。可以使用在命令行中调用任何命令。 Docker通过读取Dockerfile
中的指令自动生成映像。
docker build
命令用于从Dockerfile构建映像。
#规范:
Dockerfile命名D必须大写其他必须小写。
Dockerfile中所有的指令必须大写
#构建镜像的命令:
docker build [OPTIONS] PATH
#参数:
-t name:tag : 指定构建镜像的名称
docker build -t nginx:v2 . # . 当前目录
二、Dockerfile的基本结构
Dockerfile 一般分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令,# 为 Dockerfile 中的注释。
一张图解释常用指令的意义:
三、Dockerfile文件说明
Docker以从上到下的顺序运行Dockerfile的指令。为了指定基本映像,第一条指令必须是FROM。一个声明以#
字符开头则被视为注释。可以在Docker文件中使用RUN
,CMD
,FROM
,EXPOSE
,ENV
等指令。
在这里列出了一些常用的指令。
1.FROM:指定基础镜像,必须为第一个命令
#格式:
FROM <image>
FROM <image>:<tag>
FROM <image>@<digest>
#示例:
FROM mysql:5.6
#注:tag或digest是可选的,如果不使用这两个值时,会使用latest版本的基础镜像
2.MAINTAINER: 维护者信息
#格式:
MAINTAINER <name>
#示例:
MAINTAINER Jasper Xu
MAINTAINER sorex@163.com
MAINTAINER Jasper Xu <sorex@163.com>
3.RUN:构建镜像时执行的命令
#RUN用于在镜像容器中执行命令,其有以下两种命令执行方式:
shell执行
#格式:
RUN <command>
#示例:
[root@docker /etc/docker]# cat > Dockerfile << EOF
FROM nginx:1.21.1
RUN touch /usr/share/nginx/1.txt
EOF
[root@docker /etc/docker]# docker build -t nginx:v3 .
[root@docker /etc/docker]# docker run -it --rm nginx:v3 bash
root@8fee06507b90:/# ls /usr/share/nginx/
1.txt html
#注:运行的命令必须是基础镜像中包含的命令,执行的结果直接保存在景象中
4.ADD
#格式:
ADD <src>... <dest>
ADD ["<src>",... "<dest>"] 用于支持包含空格的路径
#示例:
ADD hom* /mydir/ # 添加所有以"hom"开头的文件
ADD hom?.txt /mydir/ # ? 替代一个单字符,例如:"home.txt"
ADD test relativeDir/ # 添加 "test" 到 `WORKDIR`/relativeDir/
ADD test /absoluteDir/ # 添加 "test" 到 /absoluteDir/
5.COPY
#功能描述:复制文件到镜像中
#语法:
COPY < src>… < dest>|[“< src>”,… “< dest>”]
#提示:指令逻辑和ADD十分相似,同样Docker Daemon会从编译目录寻找文件或目录,dest为镜像中的绝对路径或者相对于WORKDIR的路径
#示例1:
[root@docker /etc/docker]# ll
-rw-r--r--. 1 root root 67 Oct 22 14:44 daemon.json
-rw-r--r--. 1 root root 101 Nov 11 19:55 Dockerfile
-rw-r--r--. 1 root root 167 Nov 11 19:49 docker.tar.gz
-rw-------. 1 root root 244 Oct 22 14:44 key.json
[root@docker /etc/docker]# cat Dockerfile
FROM nginx:1.21.1
RUN touch /usr/share/nginx/1.txt
ADD docker.tar.gz /opt
COPY docker.tar.gz /tmp
[root@docker /etc/docker]# docker build -t nginx:v2 .
[root@docker /etc/docker]# docker run -it --rm nginx:v2 bash
root@7034bec837ab:/# ls -l /opt
total 4
-rw-r--r--. 1 root root 52 Nov 11 11:31 Dockerfile
root@7034bec837ab:/# ls -l /tmp
total 4
-rw-r--r--. 1 root root 167 Nov 11 11:49 docker.tar.gz
# ADD 支持自动解压功能,COPY 不支持
# ADD 支持通过URL下载文件(不会自动解压文件),COPY 不支持
#示例2:
[root@docker /etc/docker]# cat Dockerfile
FROM nginx:1.21.1
ADD https://mirrors.aliyun.com/centos/7.9.2009/extras/x86_64/Packages/etcd-3.2.28-1.el7_8.x86_64.rpm?spm=a2c6h.13651111.0.0.10bd2f70pV7Kl6&file=etcd-3.2.28-1.el7_8.x86_64.rpm /opt
COPY https://mirrors.aliyun.com/centos/7.9.2009/extras/x86_64/Packages/etcd-3.2.28-1.el7_8.x86_64.rpm?spm=a2c6h.13651111.0.0.10bd2f70pV7Kl6&file=etcd-3.2.28-1.el7_8.x86_64.rpm /tmp
[root@docker /etc/docker]# docker build -t nginx:v4 .
-- --- ----- 省略 ------ ------- --------
---> 886df84dcc0d
Step 6/6 : COPY https://mirrors.aliyun.com/centos/7.9.2009/extras/x86_64/Packages/etcd-3.2.28-1.el7_8.x86_64.rpm?spm=a2c6h.13651111.0.0.10bd2f70pV7Kl6&file=etcd-3.2.28-1.el7_8.x86_64.rpm /tmp
COPY failed: source can't be a URL for COPY
6.CMD:构建容器后调用,也就是在容器启动时才进行调用
# exec格式:
CMD ["executable","param1","param2"] (执行可执行文件,优先)
# shell 格式:
CMD ["param1","param2"]
#示例1:
[root@docker /etc/docker]# cat Dockerfile
FROM nginx:1.21.1
CMD ["nginx","-g","daemon off;"]
[root@docker /etc/docker]# docker build -t nginx:v1 .
[root@docker /etc/docker]# docker run -d nginx:v1
54a5e03d4f6f663a5ca40beb4e247d335b95b789bb50218b5c8d14ae401a6bad
[root@docker /etc/docker]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
54a5e03d4f6f nginx:v1 "/docker-entrypoint.…" 5 seconds ago Up 4 seconds 80/tcp laughing_easley
#示例2:
[root@docker /etc/docker]# cat Dockerfile
FROM nginx:1.21.1
CMD nginx -g 'daemon off;'
[root@docker /etc/docker]# docker build -t nginx:v2 .
[root@docker /etc/docker]# docker run -d nginx:v2
[root@docker /etc/docker]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
adce915fa2a1 nginx:v2 "/docker-entrypoint.…" 4 seconds ago Up 4 seconds 80/tcp dreamy_wright
#注:CMD不同于RUN,CMD用于指定在容器启动时所要执行的命令,而RUN用于指定镜像构建时所要执行的命令。
7.ENTRYPOINT:配置容器,使其可执行化。配合CMD可省去"application",只使用参数
#格式:
ENTRYPOINT ["executable", "param1", "param2"] (可执行文件, 优先)
ENTRYPOINT command param1 param2 (shell内部命令)
#示例:
FROM centos:7
ENTRYPOINT ["ping"]
CMD ["127.0.0.0.1"]
#注:ENTRYPOINT与CMD非常类似,不同的是通过docker run执行的命令不会覆盖ENTRYPOINT,而docker run命令中指定的任何参数,都会被当做参数再次传递给ENTRYPOINT。如果ENTPYPOINT与CMD同时存在,则CMD的相关内容会被设置为ENTPYPOINT的参数
8.LABEL:用于为镜像添加元数据
#格式:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
#示例:
LABEL version="1.0" description="这是一个Web服务器" by="IT笔录"
#注:使用LABEL指定元数据时,一条LABEL指定可以指定一或多条元数据,指定多条元数据时不同元数据之间通过空格分隔。推荐将所有的元数据通过一条LABEL指令指定,以免生成过多的中间镜像。
9.ENV:设置环境变量
#格式:
ENV <key> <value> #<key>之后的所有内容均会被视为其<value>的组成部分,因此,一次只能设置一个变量
ENV <key>=<value> ... #可以设置多个变量,每个变量为一个"<key>=<value>"的键值对,如果<key>中包含空格,可以使用\来进行转义,也可以通过""来进行标示;另外,反斜线也可以用于续行
#示例:
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat=fluffy
10.EXPOSE:指定于外界交互的端口
#格式:
EXPOSE <port> [<port>...]
#示例:
EXPOSE 80 443
EXPOSE 8080 EXPOSE 11211/tcp 11211/udp
#注:EXPOSE并不会让容器的端口访问到主机。要使其可访问,需要在docker run运行容器时通过-p来发布这些端口,或通过-P参数来发布EXPOSE导出的所有端口
11.VOLUME:用于指定持久化目录
#格式:
VOLUME ["/path/to/dir"]
#示例:
[root@docker /etc/docker]# cat Dockerfile
FROM nginx:1.21.1
VOLUME /usr/share/nginx
[root@docker /etc/docker]# docker build -t nginx:v1 .
[root@docker /etc/docker]# docker run -itd nginx:v1 bash
# VOLUME /usr/share/nginx 在 /var/lib/docker/volumes目录下随机映射
#查看
[root@docker ~]# docker inspect sleepy_wozniak
"Mounts": [
{
"Type": "volume",
"Name": "462a95793887840bf134192df56117eb2f4ac88e83d80a4e4a99677bf2a4ab98",
"Source": "/var/lib/docker/volumes/462a95793887840bf134192df56117eb2f4ac88e83d80a4e4a99677bf2a4ab98/_data",
"Destination": "/usr/share/nginx",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
#参数:
-v :指定挂载目录
#将宿主机的/opt/目录挂载到容器中的/usr/share/nginx
[root@docker /etc/docker]# docker run -itd -v /opt/:/usr/share/nginx nginx:v1 bash
[root@docker ~]# docker exec -it recursing_dijkstra bash
root@c587e91b92be:/# ls -l /usr/share/nginx
total 0
drwx--x--x. 4 root root 28 Oct 22 06:44 containerd
drwxr-xr-x. 2 root root 6 Oct 27 06:31 demo
drwxr-xr-x. 2 root root 6 Oct 25 12:22 html5-mario
#查看
[root@docker ~]# docker inspect recursing_dijkstra
"Mounts": [
{
"Type": "bind",
"Source": "/opt",
"Destination": "/usr/share/nginx",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
12.WORKDIR:工作目录,类似于cd命令
#设置工作目录,运行应用程序时的起始目录(默认是根目录)
#格式:
WORKDIR /path/to/workdir
#示例:
[root@docker /etc/docker]# cat Dockerfile
FROM nginx:1.21.1
VOLUME /usr/share/nginx
[root@docker /etc/docker]# docker build -t nginx:v1 .
[root@docker /etc/docker]# docker run -itd --name nginx01 nginx:v1 bash
[root@docker /etc/docker]# docker exec -it nginx01 bash
root@44c95ab16ca8:/usr/share/nginx# pwd
/usr/share/nginx
#注:通过WORKDIR设置工作目录后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT、ADD、COPY等命令都会在该目录下执行。在使用docker run运行容器时,可以通过-w参数覆盖构建时所设置的工作目录。
13.USER
#指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。使用USER指定用户时,可以使用用户名、UID或GID,或是两者的组合。当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户
#格式:
USER user
USER user:group
USER uid
USER uid:gid
USER user:gid
USER uid:group
#示例:
USER www
#注:使用USER指定用户后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT都将使用该用户。镜像构建完成后,通过`docker run`运行容器时,可以通过-u参数来覆盖所指定的用户。
14.ARG:用于指定传递给构建运行时的变量
#格式:
ARG <name>[=<default value>]
#示例1:
[root@docker /etc/docker]# cat Dockerfile
FROM centos:7
ARG PACKAGE=zip
RUN yum install -y $PACKAGE
[root@docker /etc/docker]# docker build -t nginx:v1 .
--- ---- 省略 ---- --- --
Installed:
zip.x86_64 0:3.0-11.el7
Complete!
Removing intermediate container 29ccd5414e32
---> 4890c73d3959
Successfully built 4890c73d3959
Successfully tagged nginx:v1
#示例2: 安装软件自定义
[root@docker /etc/docker]# cat Dockerfile
FROM centos:7
ARG PACKAGE
RUN yum install -y $PACKAGE
[root@docker /etc/docker]# docker build --build-arg=PACKAGE=zsh -t nginx:v2 .
------ ---- 省略 ---- ---- ---
Installed:
zsh.x86_64 0:5.0.2-34.el7_8.2
Complete!
Removing intermediate container a98ffcee77e9
---> eed93606434c
Successfully built eed93606434c
Successfully tagged nginx:v2
15.ONBUILD:用于设置镜像触发器
#格式:
ONBUILD [INSTRUCTION]
#示例:
[root@docker /etc/docker]# cat Dockerfile
FROM centos:7
ONBUILD RUN mkdir /test
[root@docker /etc/docker]# docker build -t nginx:v1 .
[root@af8cd29dcbb2 /]# ls -l | grep test
[root@docker /etc/docker]# cat Dockerfile
FROM nginx:v1
RUN mkdir /old
[root@docker /etc/docker]# docker build -t nginx:v2 .
[root@docker /etc/docker]# docker run -it --rm nginx:v2 bash
[root@fa9f9b35571b /]# ls -l | grep -E "test|old"
drwxr-xr-x. 2 root root 6 Nov 12 08:02 old
drwxr-xr-x. 2 root root 6 Nov 12 08:02 test
#注:ONBUILD后面跟指令,在构建时不会执行。当所构建的镜像被用做其它镜像的基础镜像,该镜像中的触发器将会被触发
四、使用Dockerfile构建部署discuz
1.构建MySQL镜像
#下载MySQL二进制包
#官网二进制包下载地址:https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.36-linux-glibc2.12-x86_64.tar.gz
#因为网络问题,我这里是下载的阿里云镜像站的MySQL二进制包
[root@docker /etc/docker]# wget https://mirrors.cloud.tencent.com/mysql/downloads/MySQL-5.7/mysql-5.7.36-linux-glibc2.12-x86_64.tar.gz
#创建工作目录
[root@docker /etc/docker]# mkdir -p /discuz/mysql
[root@docker /etc/docker]# cd /discuz/mysql
[root@docker /discuz/mysql]# mv /etc/docker/mysql-5.7.36-linux-glibc2.12-x86_64.tar.gz /discuz/mysql
#配置my.cnf
[root@docker /discuz/mysql]# cat my.cnf
[mysqld]
user=mysql
basedir=/usr/local/mysql
datadir=/usr/local/mysql/data
port=3306
socket=/usr/local/mysql/mysql.sock
character-set-server=utf8mb4
log-error=/usr/local/mysql/data/mysqld.log
pid-file=/usr/local/mysql/data/mysqld.pid
[mysql]
socket=/usr/local/mysql/mysql.sock
[client]
socket=/usr/local/mysql/mysql.sock
#编写脚本
[root@docker /discuz/mysql]# cat start.sh
#!/bin/bash
if [ ! $MYSQL_ROOT_PASSWORD];then
echo "远程root密码必填!!!"
exit;
fi
nohup ./create_user.sh &
/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf
[root@docker /discuz/mysql]# cat create_user.sh
#!/bin/bash
while true;do
/usr/local/mysql/bin/mysql -uroot -p'' -e 'show databases;' &>/dev/null
if [ $? -eq 0 ];then
/usr/local/mysql/bin/mysql -uroot -p${MYSQL_ROOT_PASSWORD} -h${HOSTNAME} -e 'show databases;' &>/dev/null
if [ $? -eq 0 ];then
exit
fi
/usr/local/mysql/bin/mysql -uroot -p'' -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' WITH GRANT OPTION;FLUSH PRIVILEGES;" &>/dev/null
exit
fi
sleep 1;
done
[root@docker /discuz/mysql]# chmod +x create_user.sh
[root@docker /discuz/mysql]# chmod +x start.sh
#配置构建MySQL的Dockerfile
[root@docker /discuz/mysql]# cat Dockerfile
FROM centos:7
RUN useradd -M -s /sbin/nologin -r mysql
RUN yum install -y ncurses-devel libaio-devel gcc gcc-c++ numactl libaio glibc cmake autoconf
ADD mysql-5.7.36-linux-glibc2.12-x86_64.tar.gz /usr/local/
RUN ln -s /usr/local/mysql-5.7.36-linux-glibc2.12-x86_64 /usr/local/mysql
RUN chown -R mysql.mysql /usr/local/mysql/
RUN mkdir -p /usr/local/mysql/data
RUN chown -R mysql.mysql /usr/local/mysql
RUN /usr/local/mysql/bin/mysqld --initialize-insecure --user=mysql --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data
ADD my.cnf /etc/
WORKDIR /usr/local/mysql
EXPOSE 3306
ADD start.sh /usr/local/mysql
ADD create_user.sh /usr/local/mysql
CMD ./start.sh
#构建镜像
[root@docker /discuz/mysql]# docker build -t mysql:v1 .
#启动mysql容器测试
[root@docker /discuz/mysql]# docker run -d -P -e MYSQL_ROOT_PASSWORD=123 mysql:v1
[root@docker /discuz/mysql]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2b580fb4e3bd mysql:v2 "/bin/sh -c ./start.…" About a minute ago Up 41 seconds 0.0.0.0:4053->3306/tcp interesting_lehmann
[root@docker /discuz/mysql]# docker exec -it 2b580fb4e3bd bash
[root@3b0f31c3f641 mysql-5.7.36-linux-glibc2.12-x86_64]# ./bin/mysql -uroot -p123 -2b580fb4e3bd
mysql>
#使用Navicat测试连接
10.0.0.71:4053
2.构建nginx+php镜像
[root@docker /data]# unzip Discuz_X3.4_SC_UTF8_20210926.zip
[root@docker /data]# mv upload /discuz/nginx+php/
#构建镜像
[root@docker /discuz/nginx+php]# cat Dockerfile
FROM centos:7
RUN useradd -M -s /sbin/nologin -r www
ADD nginx.repo /etc/yum.repos.d/
RUN yum install -y nginx
RUN sed -i 's#user nginx#user www#g' /etc/nginx/nginx.conf
ADD discuz.conf /etc/nginx/conf.d/
RUN yum remove php-mysql-5.4 php php-fpm php-common
RUN rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
RUN rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm
ADD php.repo /etc/yum.repos.d/
RUN yum install -y php72w php72w-cli php72w-common php72w-devel php72w-embedded php72w-gd php72w-mbstring php72w-pdo php72w-xml php72w-fpm php72w-mysqlnd php72w-opcache php72w-pecl-memcached php72w-pecl-redis php72w-pecl-mongodb
RUN sed -i 's#apache#www#g' /etc/php-fpm.d/www.conf
EXPOSE 80 443
CMD php-fpm && nginx -g 'daemon off;'
#启动容器测试
[root@docker /discuz/nginx+php]# docker run -d -P php-nginx:v1
[root@docker /discuz/nginx+php]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6459eb2c0c5a php-nginx:v1 "/bin/sh -c 'php-fpm…" 3 seconds ago Up 2 seconds 0.0.0.0:4052->80/tcp, 0.0.0.0:4051->443/tcp fervent_robinson
#删除所有测试容器
[root@docker /discuz/nginx+php]# docker rm -f $(docker ps -aq)
3.创建网桥
#创建dis网桥
[root@docker /discuz/nginx+php]# docker network create dis
#创建容器
[root@docker /discuz/nginx+php]# docker run -d --network dis --name mysql -e MYSQL_ROOT_PASSWORD=123 mysql:v1
[root@docker /discuz/nginx+php]# docker run -d --name nginx -p 80:80 --network dis -v /discuz/nginx+php/upload/:/usr/share/nginx/html php-nginx:v1
[root@docker /discuz/nginx+php]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
017a58d6a827 php-nginx:v1 "/bin/sh -c 'php-fpm…" 5 seconds ago Up 2 seconds 0.0.0.0:80->80/tcp, 443/tcp nginx
35c458d7c2d3 mysql:v1 "/bin/sh -c ./start.…" About a minute ago Up About a minute 3306/tcp mysql
#本地hosts配置
10.0.0.71 linux.discuz.com
#浏览器访问
linux.discuz.com
#文件列表
[root@docker /discuz]# tree -L 2 -C
.
├── mysql
│ ├── create_user.sh
│ ├── Dockerfile
│ ├── my.cnf
│ ├── mysql-5.7.36-linux-glibc2.12-x86_64.tar.gz
│ └── start.sh
└── nginx+php
├── discuz.conf
├── Dockerfile
├── nginx.repo
├── php.repo
└── upload