Skip to content

Layered Spring Boot Docker images #292

Open

Description

@uniquejava

阅读前必读:

  1. Best practices for writing Dockerfiles最佳实践
  2. Docker image为什么要分层

当前的spring boot Dockerfile配置

Dockerfile

FROM adoptopenjdk/openjdk8:ubi-jre
EXPOSE 9090
RUN mkdir /opt/app
ADD target/myapp.jar /opt/app/app.jar
ENTRYPOINT [ "sh", "-c", "java -Dspring.profiles.active=dev -jar /opt/app/app.jar" ]

Tips:spec中的command能覆盖Dockerfile中的ENTRYPOINT.

k8s-myapp-dev.yaml

spec:
  restartPolicy: Always
  containers:
    - image: jp.icr.io/myorg/myapp:1.0.15
      imagePullPolicy: Always
      command:
        [
          "sh",
          "-c",
          "java -Dspring.profiles.active=dev -jar /opt/app/app.jar",
        ]
      name: myapp
      volumeMounts:
        - name: logs-persistent-storage
          mountPath: /logs

当前build和push的方式

详见: https://cloud.ibm.com/kubernetes/registry/main/start

# Log in to your IBM Cloud account.
ibmcloud login -a https://cloud.ibm.com --sso

# Ensure that you're targeting the correct IBM Cloud Container Registry region.
ibmcloud cr region-set ap-north

# Choose a name for your first namespace, and create that namespace.
ibmcloud cr namespace-add <my_namespace>

# Log your local Docker daemon into the IBM Cloud Container Registry.
ibmcloud cr login

# 查看当前有哪些image (找到最新image的版本号)
ibmcloud cr images

# 做一个更新的image (注意每次将版本号x加1)
export VERSION=1.0.1
# 在本地编译java项目
cd myapp
mvn clean package -DskipTests=true
docker build -t myorg/myapp:$VERSION .

# 打两个tag
docker tag myorg/myapp:$VERSION jp.icr.io/myorg/myapp:$VERSION

# 切换到 Context A
ibmcloud ks cluster config --cluster xxxxxxxxx
# 推送到 A 环境
docker push jp.icr.io/myorg/myapp:$VERSION
# 修改 k8s-myapp-dev.yaml中的版本号
vi ../kubernetes/development/k8s-myapp-dev.yaml
# 重新apply
kubectl apply -f ../kubernetes/development/k8s-myapp-dev.yaml

存在的问题

docker images
docker history myorg/myapp:$VERSION

Dockerfile中的每一条指令构成一个新的layer, 通过docker history命令可以看到 ADD target/myapp.jar 这一步build了一个相当大的image layer(60M)

我们需要将spring boot中不变的dependency单独build成一个独立的image layer。

References

Optimizing Spring Boot apps for Docker

https://blog.jdriven.com/2019/08/layered-spring-boot-docker-images/

Activity

  1. uniquejava commented on Apr 3, 2020

    @uniquejava
    OwnerAuthor

    A better Dockerfile

    每一步的含义见: https://spring.io/blog/2018/11/08/spring-boot-in-a-container

    Dockerfile

    FROM adoptopenjdk/openjdk8:ubi-jre
    
    EXPOSE 9090
    
    RUN mkdir /app
    
    ARG DEPENDENCY=target/dependency
    COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
    COPY ${DEPENDENCY}/META-INF /app/META-INF
    COPY ${DEPENDENCY}/BOOT-INF/classes /app
    
    ENTRYPOINT [ "sh", "-c", "java ${JAVA_OPTS} -cp app:app/lib/* com.xxx.MyAwesomeApplication ${0} ${@}" ]
    

    像这样打包:

    mvn clean package -DskipTests=true
    mkdir target/dependency
    (cd target/dependency; tar -zxf ../*.jar)
    docker build -t myorg/myapp:$VERSION .

    新的yaml (通过yaml向docker传递JAVA_OPTS)

    参考: Define Environment Variables for a Container

        spec:
          restartPolicy: Always
          containers:
            - image: jp.icr.io/myorg/myapp:1.0.16
              imagePullPolicy: Always
              env:
                - name: JAVA_OPTS
                  value: "-Dspring.profiles.active=dev"
              name: myapp

    完整的过程

    # Log your local Docker daemon into the IBM Cloud Container Registry.
    ibmcloud cr login
    
    # 查看当前有哪些image (找到最新image的版本号)
    ibmcloud cr images
    
    # 做一个更新的image (注意每次将版本号x加1)
    export VERSION=1.0.1
    # 在本地编译java项目, dual layered, 详见 https://spring.io/blog/2018/11/08/spring-boot-in-a-container
    cd myapp
    mvn clean package -DskipTests=true
    mkdir target/dependency
    (cd target/dependency; jar -xf ../*.jar)
    docker build -t myorg/myapp:$VERSION .
    
    # 打tag
    docker tag myorg/myapp:$VERSION jp.icr.io/myorg/myapp:$VERSION
    
    # 切换到 context A
    ibmcloud ks cluster config --cluster xxxx
    # 推送到 A 集群
    docker push jp.icr.io/myorg/myapp:$VERSION
    # 修改 k8s-myapp-dev.yaml中的版本号
    vi ../kubernetes/development/k8s-myapp-dev.yaml
    # 重新apply
    kubectl apply -f ../kubernetes/development/k8s-myapp-dev.yaml
    # 查看pod状态
    kubectl get po
    # 查看spring boot启动的日志文件 (网络原因有点卡), 两个pod实例的日志交织在一起
    # 10.47.84.37 是node的内部ip地址
    ../kubernetes/development/nsenter-node.sh 10.47.84.37
    => If you dont see a command prompt, try pressing enter.
    => root@kube-xxx:~#
    => root@kube-xxx:~# tail -f /mnt/myapp/logs/sa.log (查看后台日志文件)
    => root@kube-xxx:~# exit
    (按Ctrl + D)
    => logout
    => pod "cyper-nsenter-10.47.84.37" deleted
    
    # tips: 查看当前 k8s 上下文
    kubectl config get-contexts
  2. uniquejava commented on Apr 3, 2020

    @uniquejava
    OwnerAuthor

    继续优化

    将

    mkdir target/dependency
    (cd target/dependency; tar -zxf ../*.jar)

    写到pom.xml里

    详见: https://blog.jdriven.com/2019/08/layered-spring-boot-docker-images/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions