dockerfile基本介绍
什么是dockerfile
dockerfile
是一个文本文件,里面包含了一条条的指令,每一条指令都对应构建一个镜像层。
按顺序执行这些指令,就可以自动构建出一个docker
镜像。
这些镜像层是堆叠的,每一层都是基于前一层构建的增量。
dockerfile
的执行会一层层构建镜像层,最终形成完整的镜像。
dockerfile语法
-
FROMO
FROM
指令是用来指定基础镜像,基础镜像可以是官方远程仓库中的镜像,也可以是本地构建的镜像。
FROM
语法示例:FROM ubuntu:18.04
-
RUN
RUN
指令使用来执行命令,可以安装软件包、运行脚本或者设置系统配置等。
RUN
语法示例:RUN yum update && yum install python -y
-
ADD & COPY
ADD
和COPY
指令都是用来将宿主机的文件复制到镜像里面,区别在于COPY
只支持纯复制文件,而ADD
则支持压缩文件自动解压
ADD
语法示例:ADD ./app.py /soft/app.py
COPY
语法示例:COPY ./app.py /soft/app.py
-
WORKDIR
WORKDIR
指令是用来设置工作目录,后续的RUN、CMD、ENTRYPOINT、COPY、ADD
指令都会在这个目录下执行。
WORKDIR
语法示例:WORKDIR /data
-
ENV
ENV
指令用来设置环境变量。你可以在之后直接使用这个环境变量,也可以在之后的RUN、CMD、ENTRYPOINT
指令中使用。
ENV
语法示例:ENV PG_MAJOR 9.3 RUN yum install postgresql-${PG_MAJOR} -y
-
ARGS
# 固定写死版本,不够灵活
FROM golang:1.20-alpine
# 修改为如下内容
ARG GO_VERSION=1.20
FROM golang:${GO_VERSION}-alpine
# 构建时可以修改对应的版本,不修改则使⽤ARG变量的值
docker build --build-arg="GO_VERSION=1.19"
-
EXPOSE
EXPOSE
指令是用来声明镜像暴露的端口,这只是一个声明,在运行容器时还需要用-p
来映射宿主机端口关联至对应容器的端口。
EXPOSE
语法示例:EXPOSE 80、EXPOSE 80 443
-
CMD
CMD
指定容器启动时执行的命令,但CMD
指令智能有一条,如果定义多条命令,则只有最后一条会被执行。
还需要注意的是,在启动容器时,我们可以手动指定启动的参数,替换掉CMD
的启动命令。
CMD
语法示例:CMD ["python", "app.py"]
-
ENTRYPOINT
ENTRYPOINT
指令和CMD
类似,都是用来指定容器启动时要执行的命令,他不会被启动容器时指定的参数所替代,也就是无论如何它都会执行。
1.ENTRYPOINT
语法示例:ENTRYPOINT ["python", "app.py"]
2.ENTRYPOINT
结合CMD
使用ENTRYPOINT ["python"] CMD ["app.py"] # CMD的内容会作为ENTRPOINT指令的参数被传递
3.然后我们可以运行容器指定特定参数
docker run -it --rm myimage # 直接启动容器,会运⾏ python app.py docker run -it --rm myimage app2.py # 启动容器,传递参数,会运⾏ python app2.py,因为在启动容器时覆盖了CMD
-
HEALTHCHECK
HEALTHCHECK
指令是用来检测容器内执行的应用程序是否处于健康状态;
HEALTHCHECK
语法示例:HEALTHCHECK [OPTIONS] CMD command
例如:每30s
左右检查一次网站服务是否能够在三秒内提供访问(默认--retries
尝试3次,如果都不成功则失败)HEALTHCHECK --interval=30s --timeout=3s \ CMD curl -f http://localhost/ || exit 1 # 命令的退出状态表示容器的健康状态。可能的值是: # 0:成功 - 容器健康并可以使⽤ # 1:不健康——容器没有正常⼯作 # 2:保留 - 不要使⽤此退出代码
dockerfile实践
构建一个基于rockylinux
环境的nginx
镜像
cd /data/test
cat Dockerfile
# 指定的基础镜像是谁
FROM rockylinux:9
# RUN要执行的命令,为镜像增加层
RUN yum install wget gcc pcre-devel zlib-devel make -y
# COPY
COPY ./epel.repo /etc/yum.repos.d/epel.repo
# ADD 复制文件,如果是压缩包,则自动解压
ENV NGX_VERSION="1.26.2"
# 设定工作目录为/usr/local
WORKDIR /usr/local
ADD ./nginx-${NGX_VERSION}.tar.gz ./
# 进行编译操作
RUN mkdir -p /app && \
cd nginx-${NGX_VERSION} && \
./configure --prefix=/app/nginx-${NGX_VERSION} && \
make && make install
# 初始化动作
RUN useradd www && \
mkdir /code && \
chown -R www.www /app/nginx-${NGX_VERSION} && \
ln -s /app/nginx-${NGX_VERSION} /app/nginx
# 配置文件
WORKDIR /app/nginx/conf
COPY nginx.conf .
# 暴露端口
EXPOSE 80 443
# 准备代码
VOLUME /code
COPY index.html /code
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost || exit 1
# 启动
#CMD ["/app/nginx/sbin/nginx","-g","daemon off;"]
#CMD ['/bin/bash','-c ','/app/nginx/sbin/nginx -g daemon off']
ENTRYPOINT ["/app/nginx/sbin/nginx","-g","daemon off;"]
#ENTRYPOINT ["tail","-f"]
#CMD ["/etc/hosts"]
-
创建镜像
docker build -t test:v1.0 .
-
运行容器
docker run -d -P -it test:v1.0
-
查看挂载目录
docker inspect 47
"Mounts": [
{
"Type": "volume",
"Name": "acff48924f78b659e1a82ddb69cf03303c15dcaddf2eae16c45d393d0cd66cd4",
"Source": "/data/docker/volumes/acff48924f78b659e1a82ddb69cf03303c15dcaddf2eae16c45d393d0cd66cd4/_data",
"Destination": "/code",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
- 测试效果
curl localhost:32776
hello world !
-
修改宿主机挂载目录
index.html
,主页内容跟着修改。 -
nginx.conf
配置内容
cat nginx.conf
user www;
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log logs/access.log main;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
root /code;
index index.html index.htm;
}
}
}
dockerfile场景实践
分层构建概念
通常docker
镜像采用分层构建的方案。
也就是首先构建基础运行环境镜像,然后在其之上依次构建中间件镜像和业务镜像。
具体可以分为以下几层:
- 基础系统镜像:选择一个官方的操作系统镜像,如
ubuntu、centos、rockylinux
等。这一层提供基础镜像。 - 语言运行镜像:基于
os
基础镜像,安装python、node.js、java
等语言的运行环境。这一层提供语言运行环境。 - 应用镜像:基于语言运行镜像,安装应用服务器如
nginx、apache、tomcat
等。这一层提供中间间和web
服务器 - 业务镜像:基于前面的层,部署业务应用代码及其依赖。这一层提供业务应用及其配置。
这种分层构建方案的好处在于:
- 1.复用:每一层可以被下层重复使用,不需要重复配置基础环境。
- 2.隔离:每一层只需要关注自身层的功能,不依赖其他层的实现细节
- 3.缓存:每一层的构建缓存可以被直接使用,提高构建效率
- 4.灵活性:不同的层可以自由组合,满足不同的使用场景。
构建基础镜像
基于rockylinux9
构建一个基础的镜像,该镜像安装一些常用工具
- 编写
Dockerfile
cat Dockerfile
# 基础镜像
FROM rockylinux:9
# 更新系统并安装常用工具
RUN yum install -y vim tcpdump procps-ng telnet net-tools git unzip iproute && yum clean all
# 设置时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
- 执行镜像构建
docker build -t harbor.dot.com/repo/rockylinux9_base:v1.0 .
构建java应用镜像
基于基础镜像构建一个openjdk-java17
的应用镜像,当然也可以自行下载oracle
的jdk
进行镜像制作
- 编写
Dockerfile
cat Dockerfile
# 基础镜像
FROM harbor.dot.com/repo/rockylinux9_base:v1.0
# 安装JAVA环境
RUN yum install java-17-openjdk java-17-openjdk-devel maven-openjdk17 -y
RUN curl -o /etc/maven/settings.xml 192.168.99.7/download/settings_docker.xml
- 构建镜像
docker build -t harbor.dot.com/repo/openjdk-17:v1.0 .
构建java业务镜像
- 编写
Dockerfile
cat Dockerfile
# 基础镜像
FROM harbor.dot.com/repo/openjdk-17:v1.0
#
RUN wget 192.168.99.7/download/springboot-devops-demo-jar-java17.tar.gz
RUN tar xf springboot-devops-demo-jar-java17.tar.gz && cd springboot-devops-demo-jar-java17/ && mvn package
RUN mv /springboot-devops-demo-jar-java17/target/oldxu-jar-1.0.0.jar oldxu-jar.jar
# 服务暴露的端口
EXPOSE 8080
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -fs http://localhost:8080 || exit 1
# 启动jar
#ENTRYPOINT ["java","-jar","/oldxu-jar.jar"]
ENTRYPOINT ["/bin/bash","-c","java -jar -Xms100m -Xmx100m /oldxu-jar.jar --server.port=8080"]
# 也可以将启动命令写入脚本文件中,让ENTRYPOINT执行脚本
-
构建镜像
docker build -t harbor.dot.com/repo/springboot:v1.0 .
-
运行业务镜像测试
docker run -d -p8080:8080 harbor.dot.com/repo/springboot:v1.0
[root@docker-node01 springboot]# curl localhost:8080
Spring Boot DevOps Demo App Jar By Oldxu
- 改
Dockerfile
,增加shell
脚本
cat Dockerfile
# 基础镜像
FROM harbor.dot.com/repo/openjdk-17:v1.0
#
RUN wget 192.168.99.7/download/springboot-devops-demo-jar-java17.tar.gz
RUN tar xf springboot-devops-demo-jar-java17.tar.gz && cd springboot-devops-demo-jar-java17/ && mvn package
RUN mv /springboot-devops-demo-jar-java17/target/oldxu-jar-1.0.0.jar oldxu-jar.jar
COPY ./entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
# 服务暴露的端口
EXPOSE 8080
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -fs http://localhost:8080 || exit 1
ENTRYPOINT ["/bin/bash","-c","/entrypoint.sh"]
shell
脚本
cat entrypoint.sh
#!/usr/bin/bash
java -jar -Xms${JVM_XMS:-50m} -Xmx${JVM_XMX:-50m} /oldxu-jar.jar --server.port=8080
- 运行镜像
docker run -d -p8080:8080 -e JVM_XMS=100m -e JVM_XMX=100m harbor.dot.com/repo/springboot:v1.0
使用官方基础镜像
https://docs.docker.com/get-started/workshop/02_our_app/
Dockerfile多阶段构建
什么是多阶段构建
docker
多阶段构建(multi-stage builds
)是一种优化docker
镜像构建过程的方法,它允许你在一个Dockerfile
中定义多个构建阶段。
在实际镜像的构建过程,我们可能会碰到编译代码和运行代码需要不同的依赖(例如:nodejs
在编译阶段需要npm
,运行阶段仅需要nginx
)。
传统的做法是在一个Dockerfile
中完成所有操作,但这样会导致镜像变得臃肿且包含很多不必要的组件。
而多阶段构建通过在Dockerfile
中使用多个FROM
语句来解决这个问题。
每个FROM
语句都定义了一个新的构建阶段,这些阶段可以互相独立,也可以互相依赖。
通过使用COPY --from
语句,我们可以在阶段之间传递文件或者数据。
多阶段构建优势
下面是一个简单的node.js
应用的Dockerfile
,该Dockerfile
包含了两个阶段:
- 1.构建阶段:使用
node.js alpine
镜像作为编译环境,安装依赖,编译应用。 - 2.运行阶段:使用
nginx
稳定版alpine
镜像作为运行环境,经build
阶段编译号的dist
目录复制至nginx
站点路径。
# 第⼀阶段:编译nodejs
FROM node:alpine AS build
WORKDIR /app
COPY package.json .
RUN npm install
COPY . .
RUN npm run build
# 第⼆阶段:运⾏应⽤
FROM nginx:stable-alpine
COPY --from=build /app/dist /usr/share/nginx/html
这样,最终的nginx
镜像只包含编译结果,而不会将node.js
环境和源代码都复制进来,大大减少了镜像大小。
同时也实现了编译环境和运行环境的隔离。
golang多阶段构建
例:构建一个运行go
语言程序的镜像。
我们需要先编译go
代码,然后将编译后的可执行文件放到一个轻量级的运行环境中。
- 编写
go
代码
[root@docker-node01 go]# cat main.go
package main
import "fmt"
func main() {
fmt.Println("Hello, Monday!")
}
- 编写
Dockerfile
cat Dockerfile
# 第一阶段,编译代码
# 基础镜像
FROM golang:1.16 AS build
# 指定工作目录
WORKDIR /app
# 拷贝代码至工作目录
COPY . .
# 编译main.go 为myapp
RUN go build -o myapp main.go
# 第二阶段,运行go项目
FROM alpine:latest
# 将构建阶段/app目录下的项目拷贝到该容器的/目录
COPY --from=build /app/myapp /myapp
# 运行项目
ENTRYPOINT ["sh","-c","/myapp"]
-
构建镜像
docker build -t harbor.dot.com/repo/golang_hello:v1.0 .
-
运行镜像
docker run --rm -it harbor.dot.com/repo/golang_hello:v1.0
-
检查编译环境大小和运行环境镜像大小
docker images | grep golang
harbor.dot.com/repo/golang_hello v1.0 dce6848a2c33 3 minutes ago 9.74MB
golang 1.16 972d8c0bc0fc 2 years ago 920MB
java多阶段构建
- 下载代码
cd /data/java-multi
wget 192.168.99.7/download/springboot-devops-demo-jar-java17.tar.gz
- 编写
Dockerfile
cat Dockerfile
# 第一个阶段构建
FROM harbor.dot.com/repo/openjdk-17:v1.0 AS build
# 编译Java项目
ADD ./springboot-devops-demo-jar-java17.tar.gz /
RUN cd /springboot-devops-demo-jar-java17 && \
mvn package
# 第二个阶段构建
FROM openjdk:17-jdk-alpine
COPY --from=build /springboot-devops-demo-jar-java17/target/*.jar /oldxu-jar.jar
EXPOSE 8080
ENTRYPOINT ["sh","-c","java -jar /oldxu-jar.jar --server.port=8080"]
-
构建镜像
docker build -t harbor.dot.com/repo/springboot:v2.0 .
-
启动并测试镜像
docker run -d -p8080:8080 harbor.dot.com/repo/springboot:v2.0
curl localhost:8080
Spring Boot DevOps Demo App Jar By Oldxu
- 检查此前构建的
java
业务镜像以及现在多阶段构建的java
业务镜像
docker images | grep springboot
harbor.dot.com/repo/springboot v2.0 1fe1f786d411 8 minutes ago 345MB
harbor.dot.com/repo/springboot v1.0 77f0b63a35f5 2 hours ago 962MB
Docker多架构
多架构构建镜像
多架构,指的并非是不同的操作系统,而指的是不同的cpu
架构。
我们可以根据不同的cpu
架构,构建docker
镜像。
最常见的架构有:
AMD64
:intel
和amd
的64位cpu
ARM64
:macos m1
芯片使用的架构
如果你使用的是mac
的m1
芯片,通过docker
运行一个容器,并不会有任何问题,这是因为docker
会根据当前的cpu
架构选择对应的镜像架构。
同时也不需要进行额外的命令修改,就可以在m1
芯片上直接运行docker
。
构建工具buildx
使用buildx
工具可以帮助我们构建多个不同的cpu
架构平台的镜像。
它会自动拉取一个镜像运行为容器,而后在容器中对不同的cpu
架构进行分别构建,好处就是无需对Dockerfile
进行任何修改,仅需要传递一个--platform
的标志给构建命令,指定要构建的架构
-
通过以下命令为
linux/arm/v7
平台构建镜像
docker buildx build --platform=linux/arm/v7 -t harbor.dot.com/repo/springboot:v1.0 .
-
默认构建驱动程序并不支持多平台构建,需要切到另一个构建起,它支持并发构建多平台的驱动程序。
# 创建⼀个docker-container类型的构建器
[root@docker-node1 ~]# docker buildx create --driver=docker-container --name=container
container
# 列出⽬前可⽤的构建器
[root@docker-node1 ~]# docker buildx ls
新构建器container
的状态是非活动的。这是因为还没有开始使用它。
buildx构建实践
仿真构建指的是在一种架构(通常是x86
)的机器上,模拟另一种架构(例如arm
)的运行环境,然后再该环境中编译程序。
这种编程出的程序可以在目标架构上直接运行。
- 使用
buildx
将springboot
镜像构建支持多平台,额外需要添加如下两个选项
--builder=container # 选择哪个构建器
--platform=linux/amd64,linux/arm/v7,linux/arm64/v8 # ⼀次构建多个架构
-
准备
dockerfile
文件,而后执行构建命令。 -
检查构建结果。
留言