首页 > 其他分享 >手搓Docker-Image-Creator(DIC)工具(04):DIC的代码实现

手搓Docker-Image-Creator(DIC)工具(04):DIC的代码实现

时间:2024-04-04 20:30:56浏览次数:18  
标签:04 Creator appName echo sh fi 镜像 DIC config

此系列的前 3 篇主要是介绍了 Docker 的应用、Docker 编排文件 Dockerfile 的常用命令、以及 Docker 镜像的构建过程等都进行简单介绍。尤其在第 3 篇,讲述了 Docker 运行时、安装用等资源,并在文末提出了存在的不足和改进的方向,本篇就直接从代码开始介绍如何使用 DIC 工具来创建 Docker 镜像。

1 了解框架目录结构

1.1 框架根目录 docker-image-creator(目录可以随意取)

图1  简洁的第一层目录结构

图1 简洁的第一层目录结构

第一级很简洁,只有 3 个文件和 1 个文件夹,分别是:

  • framework 文件夹,是整个工具的基础,用于存放 DIC 工具的资源和工具脚本
  • createApp.sh 文件,根据相关参数信息创建一个应用项目
  • build.sh 文件,根据 Dockerfile 文件的设定,对应用项目进行构建,最终生成 Docker 镜像
  • run.sh 文件,将制定的镜像在 Docker 里以容器的形式运行

1.2 framework 目录结构

既然 framework 是基础,那就重点看看此目录下的内容,如下图:
图2  framework 子目录构成

图2 framework 子目录构成

framework 目录下包含了三个目录,分别是:

  • install - 用来存放最终拷入到镜像里的安装包(安装结束后,最好从镜像里删除)
  • runtime - 用来存放最终考入到镜像里的运行时文件,不能从镜像里删除
  • versions - 用来存放不同版本的安装安装工具包,就是脚本文件,实现多版本的支持。这个版本号需要在创建项目时选择好,并记录在应用的配置文件 config.sh 文件里

下图展示 framework 的目录结构全貌:
图3  framework 的目录结构全貌

图3 framework 的目录结构全貌

在此实例中:

  • install目录下有 1 个安装包 glibc,并区分版本目录,便于存取
  • runtime目录下有 1 个运行时文件,并区分版本目录,便于存取
  • versions目录下有 1 个安装工具包,并区分版本目录,便于选择不同版本的管理脚本

2 创建应用项目

使用该框架的第一步就是要创建一个应用项目,由 createApp.sh 文件来完成,该文件会根据输入的参数信息创建一个应用项目。

2.1 createApp.sh 文件

目前的脚本并不复杂,所以没有使用函数,直接将脚本内容写在 createApp.sh 文件里。

#!/bin/bash

# 用法:./createApp.sh [creatorVersion] [appName] [appVersion]
# 参数
# creatorVersion=$1 - 生成器版本号
# appName=$2 - 应用名称
# appVersion=$3 - 应用版本号

set -e # 遇到错误时退出脚本

# 生成器版本号
creatorVersion=$1
if [ -z "$creatorVersion" ]; then
  echo "请输入生成器版本号"
  exit 1
fi

# 应用名称
appName=$2
if [ -z "$appName" ]; then
  echo "请输入应用名称"
  exit 2
fi

# 应用版本号
appVersion=$3
if [ -z "$appVersion" ]; then
  echo "请输入应用版本号"
  exit 3
fi

# 检查版本号在 framework 目录下是否存在
if [ ! -d "framework/versions/$creatorVersion" ]; then
  echo "生成器版本 $creatorVersion 不存在!创建应用失败!!"
  exit 4
fi

# 调用指定版本下的 _createApp.sh 脚本,需要根据 _createApp.sh 脚本要求传参
. ./framework/versions/$creatorVersion/tools/_createApp.sh $1 $2 $3

从createApp.sh脚本的内容分析,其核心作用在于接收并验证用户传递的参数,然后根据指定的版本信息调用位于 framework/versions/ 目录下对应版本的 _createApp.sh 子脚本来执行实际的项目创建操作。换言之,createApp.sh扮演了引导和参数预处理的角色,而具体的项目构建逻辑实现在各个版本的 _createApp.sh 中。

同样的模式也体现在其他相关脚本中,如build.sh和run.sh。build.sh脚本负责调用对应的 _build.sh 子脚本进行构建任务,而run.sh脚本则是通过调用 _run.sh 子脚本来执行项目的运行操作。这些主脚本与子脚本之间的关系构成了一个模块化的执行结构,每个主脚本作为对外接口统一处理输入和调度,而具体功能的实现则分散在各版本的子脚本中。

这样设计的好处是,可以充分发挥各个版本的功能模块化,每个版本都可以独立扩展和修改,而不会影响其他版本。

2.2 _createApp.sh 文件

_createApp.sh 文件的代码如下:

#!/bin/bash

# 用法:./_createApp.sh [creatorVersion] [appName] [appVersion]

set -e # 遇到错误时退出脚本

# 创建应用目录
mkdir -p apps/$appName # 创建应用目录

# 如果 Dockfile 文件不存在,则创建一个模版
if [ ! -f "apps/$appName/Dockerfile" ]; then
  echo "FROM busybox:latest" > apps/$appName/Dockerfile
fi

# 生成配置文件
echo "#!/bin/bash" > apps/$appName/config.sh
echo "appName=$appName # 应用名称" >> apps/$appName/config.sh
echo "appVersion=$appVersion # 应用版本号" >> apps/$appName/config.sh
echo "imageName=$appName # 镜像名称" >> apps/$appName/config.sh
echo "containerName=$appName # 容器名称" >> apps/$appName/config.sh
echo "creatorVersion=$creatorVersion # 生成器版本号" >> apps/$appName/config.sh

# 修改 config.sh 的可执行权限
chmod 755 apps/$appName/config.sh

echo ""
echo "*********************************************************"
echo "项目【 $appName 】创建成功!"
echo "*********************************************************"
echo ""

_createApp.sh脚本的主要执行流程如下:

  • 初始化应用目录:它会创建一个新的应用目录
  • 生成 Dockerfile 模版:如果在应用根目录下未找到 Dockerfile 文件,则自动生成一个标准的 Dockerfile 模版
  • 配置信息写入 config.sh:在应用根目录下创建并填充 config.sh 文件,其中包含了诸如应用名称、版本号、镜像名称、容器名称以及生成器版本号等关键信息,同时赋予该文件可执行权限

至此,项目的基本结构已经创建完毕。

3 创建实例程序

运行 macOS 的终端,或者 Windows 里的 Git Bash 程序(下同)。

3.1 进入 createApp.sh 文件所在的目录,运行:

./createApp.sh 0.0.1 app1 1.0.0
  • 0.0.1 为生成器版本号,由于比较“低调”就取了个“小号”,但需要在 framework/versions/ 下能找到这个版本号才可以运行
  • app1 为应用名称
  • 1.0.0 为应用版本号

运行并成功创建应用项目后,目录结构改变如下图:
图4  创建后的应用项目目录结构

图4 创建后的应用项目目录结构

从上图可以看到,创建了一个 apps 目录,改目录是所有应用的根目录,目前包含了刚创建的项目目录 app1 ,该目录下包含了 config.sh、Dockerfile 两个文件。

3.2 config.sh 文件

#!/bin/bash

appName=app1 # 应用名称
appVersion=1.0.0 # 应用版本号
imageName=app1 # 镜像名称
containerName=app1 # 容器名称
creatorVersion=0.0.1 # 生成器版本号

3.3 Dockerfile 文件(默认模板)

FROM busybox:latest

就一句话!可以理解为 Dockerfile 生成镜像时创建的 “Hello World”。

#!/bin/bash

set -e # 遇到错误时退出脚本

# 判断是否运行过配置文件 config.sh,如果未运行过,则运行配置文件
if [ -z "$configured" ]; then
  . ./config.sh
  configured=true # 设置一个标志,表示已经运行过配置文件
fi

# 检查 Docker 是否在运行
if ! docker info > /dev/null 2>&1; then
  echo "Docker 没有运行。请启动 Docker 再试!"
  exit 1
fi

# 查看指定名称和TAG的镜像是否存在
set +e
rslt=`docker images | grep -E "^$appName\s+$appVersion\s+"`
set -e
# 判断字符串是否为空
if [ -z "$rslt" ]; then
  echo "镜像不存在,请先构建镜像!"
  exit 2
fi

# 检查容器是否存在
if docker ps -a --filter "name=$containerName" | grep -q "$containerName"; then
  echo "容器存在,先停止,再删除..."
  # 容器存在,先停止,再删除
  docker stop $containerName > /dev/null 2>&1
  docker rm $containerName > /dev/null 2>&1
fi

docker run -itd --name $containerName $imageName:$appVersion
echo ""
echo "*********************************************************"
echo "成功创建容器:【 $containerName 】!"
echo "*********************************************************"
echo ""

上面的代码注释很详细,而且脚本功能也不复杂,就不多说了。

4 编译和运行脚本介绍

4.1 build.sh

#!/bin/bash

# 用法:./build.sh [appName]

# 参数
# appName=$1 - 应用名称

set -e # 遇到错误时退出脚本

# 判断是否带有参数 $1
appName=$1
if [ -z "$appName" ]; then
  echo "请输入应用名称"
  exit 1
fi

# 根据 appName 查找应用目录 appPath
appPath="apps/$appName"
echo $appPath
if [ ! -d "$appPath" ]; then
  echo "应用 $appName 不存在!"
  exit 2
fi

# 判断是否运行过配置文件 config.sh,如果未运行过,则运行配置文件
if [ -z "$configured" ]; then
  # 检查 config.sh 文件是否存在
  if [ ! -f "$appPath/config.sh" ]; then
    echo "配置文件 $appPath/config.sh 不存在!请创新创建项目或创建 config.sh 配置文件!"
    exit 3
  fi
  . $appPath/config.sh
  configured=true # 设置一个标志,表示已经运行过配置文件
fi

toolsPath=./framework/versions/$creatorVersion/tools
. $toolsPath/_build.sh # 调用 framwork 下对应版本的 _build.sh 脚本

4.2 _build.sh

#!/bin/bash

set -e # 遇到错误时退出脚本

# 判断是否运行过配置文件 config.sh,如果未运行过,则运行配置文件
if [ -z "$configured" ]; then
  . ./config.sh
  configured=true # 设置一个标志,表示已经运行过配置文件
fi

# 检查 Docker 是否在运行
if ! docker info > /dev/null 2>&1; then
  echo "Docker 没用运行。请启动 Docker 再试!"
  exit 1
fi

# 判断 Dockerfile 文件是否存在
if [ ! -f "$appPath/Dockerfile" ]; then
  echo "Dockerfile 文件不存在。请检查!"
  exit 2
fi

# 构建镜像,注意此处用到了
docker build -f $appPath/Dockerfile -t $imageName:$appVersion .
echo ""
echo "*********************************************************"
echo "成功生成 Docker 镜像:【 $imageName:$appVersion 】!"
echo "*********************************************************"
echo ""

4.3 run.sh

#!/bin/bash

# 用法:./run.sh [appName]
# 参数
# appName=$1 - 应用名称

set -e # 遇到错误时退出脚本

# 根据 appName 查找应用目录
appName=$1
if [ -z "$appName" ]; then
  echo "请输入应用名称"
  exit 1
fi
appPath="apps/$appName"
echo $appPath
if [ ! -d "$appPath" ]; then
  echo "应用 $appName 不存在!"
  exit 2
fi

# 判断是否运行过配置文件 config.sh,如果未运行过,则运行配置文件
if [ -z "$configured" ]; then
  . $appPath/config.sh
  configured=true # 设置一个标志,表示已经运行过配置文件
fi

echo $creatorVersion
toolsPath=./framework/versions/$creatorVersion/tools
echo $toolsPath

. $toolsPath/_run.sh

4.4 _run.sh

#!/bin/bash

set -e # 遇到错误时退出脚本

# 判断是否运行过配置文件 config.sh,如果未运行过,则运行配置文件
if [ -z "$configured" ]; then
  . ./config.sh
  configured=true # 设置一个标志,表示已经运行过配置文件
fi

# 检查 Docker 是否在运行
if ! docker info > /dev/null 2>&1; then
  echo "Docker 没有运行。请启动 Docker 再试!"
  exit 1
fi

# 查看指定名称和TAG的镜像是否存在
set +e
rslt=`docker images | grep -E "^$appName\s+$appVersion\s+"`
set -e
# 判断字符串是否为空
if [ -z "$rslt" ]; then
  echo "镜像不存在,请先构建镜像!"
  exit 2
fi

# 检查容器是否存在
if docker ps -a --filter "name=$containerName" | grep -q "$containerName"; then
  echo "容器存在,先停止,再删除..."
  # 容器存在,先停止,再删除
  docker stop $containerName > /dev/null 2>&1
  docker rm $containerName > /dev/null 2>&1
fi

docker run -itd --name $containerName $imageName:$appVersion
echo ""
echo "*********************************************************"
echo "成功创建容器:【 $containerName 】!"
echo "*********************************************************"
echo ""

5 运行实例程序

运行 macOS 的终端,或者 Windows 里的 Git Bash 程序,进入 DIC 框架根目录,运行 build.sh 脚本。

5.1 编译镜像

运行:

./build.sh

运行成功后,输入镜像查看命令:

docker images

会看到列举出 app1 镜像。

5.2 运行容器

运行:

./run.sh

运行成功后,输入容器查看命令:

docker ps -a

会看到列举出 app1 容器。
创建单个最简应用的测试就完成了。接下来我们来实现一个 alpine+jre 镜像。

6 再一次实现 alpine+jre 镜像

6.1 创建一个新的应用

终端进入 DCI 工具所在的目录,运行:

./createApp.sh 0.0.1 alpine-jre 1.0.0

轻松创建应用项目:alpine-jre,如下图:
图5  alpine-jre 镜像应用目录

图5 alpine-jre 镜像应用目录

6.2 修改 Dockerfile 文件

内容如下:

FROM busybox:latest
# Dockerfile
# 导入的镜像源
FROM alpine:3.10

# 作者 LABEL
LABEL maintainer="[email protected]"

# 设置工作目录
WORKDIR /opt/app

# 更新源
RUN apk update && apk upgrade

# 拷贝 jre 到镜像中
COPY framework/runtime/JRE/jre1.8.0_401 /opt/app/runtime/JRE/jre1.8.0_401/

# 拷贝 glibc 到镜像中
COPY framework/install/glibc/2.29-r0 /opt/app/install/glibc/2.29-r0/

# 安装glibc
RUN apk --no-cache add ca-certificates wget 
RUN wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub
RUN apk add /opt/app/install/glibc/2.29-r0/glibc-2.29-r0.apk
RUN apk add /opt/app/install/glibc/2.29-r0/glibc-bin-2.29-r0.apk
RUN apk add /opt/app/install/glibc/2.29-r0/glibc-i18n-2.29-r0.apk

# 删除缓存文件及 install 目录
RUN rm -rf /var/cache/apk/* /opt/app/install

# 配置环境变量
ENV JAVA_HOME=/opt/app/runtime/JRE/jre1.8.0_401
ENV CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV PATH=$JAVA_HOME/bin:$PATH

6.3 创建和镜像和运行容器

终端进入 DIC 框架根目录,运行:

./build.sh alpine-jre 

成功创建镜像:alpine-jre:1.0.0

继续运行:

./run.sh alpine-jre

成功创建容器:alpine-jre

使用 docker exec 命令直接进入容器测试:

docker exec -it alpine-jre /bin/sh

出现下面的显示即表示已进入容器内部:

/opt/app #

在此窗口输入 java 命令:

java -version 

可以看到 JRE 版本:

java version "1.8.0_401"
Java(TM) SE Runtime Environment (build 1.8.0_401-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.401-b10, mixed mode)

alpine-jre 镜像生成和测试成功。退出容器:

exit

7 总结

在此阶段,我们成功完成了DIC(Docker Image Creation)工具的开发与初步测试工作,证明了其能够有效地用于生成各类定制化镜像。用户可以根据需求灵活运用这一工具来构建其他所需的Docker镜像。

为进一步提升DIC工具的用户体验和功能性,我们可以考虑以下优化方向:

  • 图形用户界面集成:引入GUI界面,方便用户通过直观的操作选择所需资源文件,简化繁琐的命令行操作步骤。例如,用户可以直接在界面上拖拽或选择要打包进镜像的文件和目录,降低使用门槛。

  • 增强复杂操作支持:扩展DIC工具的能力,使其能够处理更为复杂的场景,如添加多阶段构建、自动依赖检测与安装、环境变量配置等功能。通过智能分析和自动化处理,减少人工编写Dockerfile时的工作量和出错率。

  • 自动化Dockerfile生成:基于用户在界面上的选择和配置,DIC工具能够自动生成规范且高效的Dockerfile,不仅提升了工作效率,还确保了Dockerfile的质量和一致性。

  • 总之,尽管目前DIC工具已达到基本可用状态,但仍存在许多潜在的改进空间。我们鼓励开发者们根据实际应用场景不断对其进行迭代升级,共同推动DIC工具向着更加便捷、强大的方向发展。期待未来能见证一个功能全面且易用性出色的DIC工具诞生!


上一篇:手搓Docker-Image-Creator(DIC)工具(03):实现alpine+jre的镜像

下一篇:没有了


标签:04,Creator,appName,echo,sh,fi,镜像,DIC,config
From: https://blog.csdn.net/weixin_42398461/article/details/137290617

相关文章

  • 信息学奥赛一本通题目解析:1204:爬楼梯(记忆化递归)
    【题目描述】树老师爬楼梯,他可以每次走1级或者2级,输入楼梯的级数,求不同的走法数。例如:楼梯一共有3级,他可以每次都走一级,或者第一次走一级,第二次走两级,也可以第一次走两级,第二次走一级,一共3种方法。【输入】输入包含若干行,每行包含一个正整数N,代表楼梯级数,1≤N≤30。【......
  • 11.python的字典dict(下) 遍历字典,结构优化
    11.python的字典dict(下)遍历所有的键值对items()方法是字典的一个内置方法,用于返回字典中所有键值对的视图(view)。它返回一个可迭代的对象,每个元素都是一个包含键和对应值的元组。下面用一个例子来说明items()方法的用法:dict1={'name':'John','age':25,'job':'En......
  • 2404 杂题记录
    1.线段树维护gcd查线段树势能提到的一个例题。线段树势能里面强调线段树维护区间gcd的时间复杂度为遍历数组的复杂度+总gcd的时间复杂度,即O(n+logC),均摊到每一个操作上就是O(1+logC/n)=O(1),所以我们可以O(nlogn)解决线段树维护区间gcd。不过网上做法好......
  • POI2005 KOS-Dicing
    网络流#二分#POI#Year2005考虑二分答案,用\(Dinic\)来\(check\)具体来说,就是对每一个人限制流量,然后检查能不能把所有场全部流满#include<bits/stdc++.h>usingnamespacestd;#defineintlonglong#defineullunsignedlonglong#defineALL(a)(a).begin(),(a).......
  • 洛谷 P1004 [NOIP2000 提高组] 方格取数
    题意:n*n的方格,从左上角到右下角两次。每一次经过的路径中,如果有数字,数字都会变成0并计数。求两次路径的最大计数。思路:线性dp,从左上角到右下角步数固定为2*n-2步。初始时0步dp[0][1][1]=grid[1][1],知道了x1和x2可以确定对应的y,可以直接进行状态转移。可以增加剪枝:x<=m......
  • 代码随想录算法训练营第一天 | 数组 704.二分查找 27.移除元素
    leetcode704.二分查找题目704.二分查找给定一个n个元素有序的(升序)整型数组nums和一个目标值target,写一个函数搜索nums中的target,如果目标值存在返回下标,否则返回-1。解题思路代码实现本题对自己的难点有大概的解题思路,但是代码实现有几个点写不出来1、怎么取......
  • 2024.04.04 网站初步搭建完成
        今天,我终于把自己耗时一年左右的时间搭建的一个网站终于初步完成了,这个网站就是咸蛋Online,这个从后端到前端都是自己一步一步摸索出来的,对于一个完全不懂前端的人来讲,过程可谓坎坷,借此,把这个过程记录下来,也和大家分享下。自己的文采不是很好,有很多想写但是写不出来的,大......
  • 备忘记录-20240404.构建服务的k8s资源清单
    导读记录一次搭建服务的成果框架graphTBC(Client)-->ig(ingress)ig-->np((nginx-php\nservice))ig-->tc((tomcat\nservice))np-->ng1(nginx)np-->ng2(nginx)ng2-..->ps((php\nservice))ng1-..->psps-->p1(PHP)ps-->p2(PHP)ps......
  • 【阿里淘天笔试题汇总】2024-04-03-阿里淘天春招笔试题(第一套)-三语言题解(CPP/Pytho
    ......
  • 【阿里淘天笔试题汇总】2024-04-03-阿里淘天春招笔试题(第二套)-三语言题解(CPP/Pytho
    ......