构建Docker Android开发基础镜像
一、需求
项目有功能需要调用 npm命令。
后端代码中使用的是 **java.lang.Runtime **类调用命令,但是默认Docker基础镜像是 openjdk:8-jre-slim,并不包含nodejs环境。
经过数次试错,最终决定更换基础镜像,更换为自己构建的包含jre和nodejs的基础镜像。
二、试错
下面是之前走过的一些弯路,也写出来让大家可以避免。
1. apt 安装 nodejs
直接在dockerfile中添加
RUN apt install nodejs npm
但是由于内网网络环境的关系,无法安装。
2. 离线安装 nodejs
大致的Dockerfile是这样的
# nodejs 环境变量
ENV NODEJS_HOME /usr/local/lib/nodejs/node-v16.13.1/bin
ENV PATH $NODEJS_HOME:$PATH
// 解压
ADD node-v16.13.1-linux-x64.tar.xz /usr/local/lib/nodejs/
WORKDIR /usr/local/lib/nodejs/
# 换源
RUN mv node-v16.13.1-linux-x64/ node-v16.13.1/ \
&& npm config set registry http://nexus.simulate.com:8081/repository/npm-group/ \
&& npm install -g hdec-mobile
一些尝试:
- 从nodejs官网下载 Linux 二进制文件,默认下载的是 .tar.xz 格式的压缩包,无奈容器中没有 xz 包,所以无法解压。
- 将 .tar.xz 在本地先解压为 .tar,再上传,构建报错 tar: Skipping to next header
- 从nodejs官网下载 .tar.gz 格式的压缩包,报错 gzip: stdin: not in gzip format
- 修改容器 apt 源为 nexus.simulate.com:8001 下的地址,但是宿主机和容器无法ping通,经过咨询蔡永健蔡哥,知道是由于docker默认网桥是172.17.x.x,nexus.simulate.com的ip也是172.17.x.x,有冲突,在ping nexus.simulate.com 的时候,会先经过网桥,不会到nexus.simulate.com,这个问题由黄艺文文姐解决之后,修改网桥为 172.19.x.x,宿主机可以ping通nexus,但是容器内使用cat命令修改/etc/apt/sources.list文件来更换apt源,报404,这个时候我的基础镜像也做好了,就没有再做深究。
三、最终方案
最终方案就是更换基础镜像,自己做一个符合需求的基础镜像。
经过查询一些资料,对比dockerhub上基础镜像的大小,决定使用 alpine 作为基础镜像,添加jre和nodejs环境,形成自己的基础镜像。
构建基础镜像的 Dockerfile 如下:
FROM alpine:latest
# 更换国内源
RUN echo "https://mirror.tuna.tsinghua.edu.cn/alpine/latest-stable/main/" > /etc/apk/repositories
RUN echo "http://mirrors.aliyun.com/alpine/latest-stable/community/" >> /etc/apk/repositories
RUN apk add wget
#dumb-init
RUN wget -O /usr/local/bin/dumb-init https://gitee.com/ji-yy/dumb-init/attach_files/928072/download/dumb-init_1.2.5_amd64
RUN chmod +x /usr/local/bin/dumb-init
#nodejs npm netstat 7zip jre
RUN apk add nodejs && apk add npm && apk add net-tools && apk add p7zip && apk add openjdk8-jre-base
ENV LANG en_US.UTF-8
ENV TZ=Asia/Shanghai
还是比较简短的,alpine 的 apk add 命令可以安装很多包,具体有哪些可以查看官方地址 Alpine Linux packages ,这里没有搜到openjdk,但是阿里和清华的国内镜像源是有的,可以查看 阿里源-alpine-community ,这里使用的也是 jdk8,与项目保持一致。
使用 docker build 命令构建后,生成基础镜像,上传到公司docker仓库,页面显示镜像大小为 76.52MB,大小还是可以接受的。
构建完基础镜像,其实项目的Dockerfile相较于凤翎默认的Dockerfile就不需要做多少改动,主要是修改 FROM 的基础镜像,改为自己的地址。
打完的项目docker镜像有 150.38MB ,可以接受。
四、补充
其实最终需求是可以在容器内执行 cordova build android 命令构建 apk,所以只是有jre和nodejs环境,还不满足android开发,需要安装完整的 jdk、android sdk、gradle等。
最终的基础镜像的Dockerfile如下。
FROM alpine:latest
# 更换国内源
RUN echo "https://mirror.tuna.tsinghua.edu.cn/alpine/latest-stable/main/" > /etc/apk/repositories
RUN echo "http://mirrors.aliyun.com/alpine/latest-stable/community/" >> /etc/apk/repositories
WORKDIR /usr/bin/
# android commandlinetools
COPY commandlinetools-linux-7583922_latest.zip androidsdk/
RUN unzip androidsdk/commandlinetools-linux-7583922_latest.zip -d androidsdk/ && rm androidsdk/commandlinetools-linux-7583922_latest.zip
ENV ANDROID_SDK_ROOT /usr/bin/androidsdk
ENV PATH ${ANDROID_SDK_ROOT}/cmdline-tools/bin:$PATH
ENV PATH ${ANDROID_SDK_ROOT}/platform-tools/bin:$PATH
WORKDIR /opt/gradle/
COPY glibc.apk /tmp/
COPY glibc-bin.apk /tmp/
#wget
RUN apk add wget && wget -O /usr/local/bin/dumb-init https://gitee.com/ji-yy/dumb-init/attach_files/928072/download/dumb-init_1.2.5_amd64 \
&& wget https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub -O /etc/apk/keys/sgerrand.rsa.pub \
&& wget -P /opt/gradle/ https://services.gradle.org/distributions/gradle-6.5-all.zip \
&& mkdir -p wrapper/dists/gradle-6.5-all/2oz4ud9k3tuxjg84bbf55q0tn/ \
&& unzip gradle-6.5-all.zip -d wrapper/dists/ \
&& mv gradle-6.5-all.zip wrapper/dists/gradle-6.5-all/2oz4ud9k3tuxjg84bbf55q0tn/ \
&& chmod +x /usr/local/bin/dumb-init \
&& apk add --no-cache /tmp/glibc.apk /tmp/glibc-bin.apk
WORKDIR /home/jdk/
#nodejs npm net-tools jdk android-tools
RUN apk add vim && apk add nodejs && apk add npm && apk add net-tools && apk add openjdk8
ENV GRADLE_USER_HOME /opt/gradle
ENV PATH $PATH:/opt/gradle/wrapper/dists/gradle-6.5/bin
ENV JAVA_HOME /usr/lib/jvm/java-1.8-openjdk
ENV PATH=$JAVA_HOME/bin:$PATH
ENV CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV LANG en_US.UTF-8
ENV TZ=Asia/Shanghai
# 安装
RUN echo y | sdkmanager --sdk_root=/usr/bin/androidsdk/ "platform-tools" "platforms;android-29" "build-tools;30.0.2" && sdkmanager --sdk_root=/usr/bin/androidsdk/ --uninstall emulator
主要是以下几处修改:
-
jre 换成 完整 jdk
-
下载 android commandlinetools,使用里面的 sdkmanager 安装 android所需的工具依赖等
-
添加 gradle
做完这些,基础镜像的体积也来到了 1.33G,后面可以研究下是否有精简的空间。
五、问题
1. 执行build报错,没有找到aapt2
参考 https://www.codenong.com/cs109720136/ ,安装缺少的三个包即可。
2. 内网环境会重新下载 gradle
查看 gradle-wrapper.properties,内容如下:
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https://services.gradle.org/distributions/gradle-6.5-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
会去 $GRADLE_USER_HOME/wrapper/dists/ 下寻找对应的压缩包。
GRADLE_USER_HOME 在 linux 下默认为 ~/.gradle
将 下载好的 gradle-6.5-all.zip 放到 wrapper/dists/gradle-6.5-all/2oz4ud9k3tuxjg84bbf55q0tn/ 下即可。
2oz4ud9k3tuxjg84bbf55q0tn 每个版本是不同的,可以参考能访问外网的主机上对应版本的文件夹名称。
3. 内网环境下需要下载maven依赖
在 nexus 增加 google() jcenter() mavenCentral() 的代理
google(): https://dl.google.com/dl/android/maven2/
jcenter(): https://jcenter.bintray.com/
mavenCentral(): https://repo.maven.apache.org/maven2
需要在能访问外网并可以访问nexus的主机上,先执行一遍 cordova build android,将所需的依赖导入nexus,才能在内网环境的服务器下载依赖。