文章目录
一、场景描述
二、问题分析
三、解决方案
一、场景描述
在采用容器化部署应用程序的过程中,发现写日志的时间是正确的东八区时间,但是容器内部采用 date 获取的时间比实际东八区的时间慢了八个小时(这里一直比较纠结这个八小时用什么动词来修饰,网上很多人用 晚 、 早,但是站在东八区来理解这两个动词,似乎都有点奇怪。纠结很久,还是以东八区的时间为参照物,来定义默认时间比东八区时间慢了八个小时吧)。
宿主机的时间
[root@~ /]# date
2020年 12月 22日 星期二 18:39:24 CST
1
2
写入日志的时间
2020-12-22 18:40:23.233 INFO [,c19fc6564922e71f,54009447514b80af,true] 1 --- [http-nio-9013-exec-10] c.m.report.common.aspect.LogAspect :
容器中的时间
[root@~ /]# docker exec -it app /bin/bash
bash-4.4# date
Tue Dec 22 10:39:54 UTC 2020
此容器的Dockerfile定义
# From 基础镜像
FROM openjdk:8-jre-alpine3.9
# 定义镜像创建者
LABEL maintainer=Rambo@xx.com
# 自定义环境变量
ENV TZ=Asia/Shanghai
# 设置本地系统时间和时区
RUN ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && echo ${TZ} > /etc/timezone
# 前端界面路径
# RUN mkdir -p /opt/java/front/sample-web
# 后端程序路径
WORKDIR /opt/java/spring-boot-sample
COPY ./*.jar ./spring-boot-sample.jar
EXPOSE 8888
ENTRYPOINT ["java", "-jar", "./spring-boot-sample.jar"]
CST China Standard Time
UTC Coordinated Universal Time
/etc/localtime 对应 date 指令本地时间
/etc/timezone 对应 java 所获取的时区
二、问题分析
通过以上 Dockerfile 可以了解到,本次实验采用的是 openjdk:8-jre-alpine3.9 最精简的版本,镜像大小 84.9M
Dockerfile 中设置本地时间和时区并写入到时间配置文件中
问题分析和研究结论:
由于 openjdk 采用的 Linux 环境的镜像是 alpine 的,其镜像特别小,集成 openjdk 也能很好的控制在 100M 左右,因此应用非常广泛,在 Docker Hub 中有大量基于 Linux alpine 版本的 JRE 镜像
# 如何验证容器采用的 Linux 内核
# 进入容器内部
docker exec -it 容器ID sh
# 查看 Linux 内核
cat /etc/issue
alpine 的镜像使用的是默认时区,并且也不存在 /usr/share/zoneinfo 路径和相关文件
为什么 Docker 容器中的日志时间却是对的呢?原因是由于以下命令将 Asia/Shanghai 配置指向了容器中的 /etc/timezone 时间,而日志输出和 JAVA 程序获取时间都是通过 /etc/timezone 这个时间配置获取的
ENV TZ=Asia/Shanghai
# 如果是基于 Alpine 的 Linux 镜像, && 前面的部分是不起作用的(因为 Alpine Linux 中都没有这个路径),所以进入容器后采用 date 查看时区还是 UTC 的时区
RUN ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && echo ${TZ} > /etc/timezone
通过以上的操作,只是将程序读取的时间设置成了东八区的时间,但是容器中的时区还是 UTC时区,如果涉及到别的程序,比如写 MySQL 的时候,就有可能存在时间不对的问题
所以,建议在构建镜像时设置了时间配置文件的同时,也需要将 Linux 时区也设置为东八区
三、解决方案
欢迎使用此镜像
FROM ramboyang/openjdk-alpine:jre-8u212-timezone-ttf
1
解决方案一
编写基于 openjdk:8-jre-alpine3.9 镜像制作包含时间组件镜像的 Dockerfile
# 基础镜像
FROM openjdk:8-jre-alpine3.9
# 设置东八区时间
ENV TZ=Asia/Shanghai
# 安装时间组件 + 安装 ttf-dejavu 解决生成验证码空指针异常问题
# 1.1 版本
# RUN apk update && apk upgrade && apk add ca-certificates && update-ca-certificates && apk add --update tzdata && cp /usr/share/zoneinfo/${TZ} /etc/localtime && echo ${TZ} > /etc/timezone && apk add --update curl bash ttf-dejavu && rm -rf /var/cache/apk/*
# 1.2 版本
# RUN apk update && apk upgrade && apk add --update tzdata && cp /usr/share/zoneinfo/${TZ} /etc/localtime && echo ${TZ} > /etc/timezone && apk add --update curl bash ttf-dejavu && rm -rf /var/cache/apk/*
# 1.3 版本
RUN apk add --update tzdata && cp /usr/share/zoneinfo/${TZ} /etc/localtime && echo ${TZ} > /etc/timezone && apk add --update ttf-dejavu && rm -rf /var/cache/apk/*
# 1.4 版本
# RUN apk add --update tzdata && cp /usr/share/zoneinfo/${TZ} /etc/localtime && echo ${TZ} > /etc/timezone && rm -rf /var/cache/apk/*
基于以上定制的 Dockerfile 构建基础镜像
# 1.1 版本
docker build -t ramboyang/openjdk-alpine:jre-8u212-cert-timezone-ttf .
# 1.2 版本
docker build -t ramboyang/openjdk-alpine:jre-8u212-timezone-ttf-bash .
# 1.3 版本
docker build -t ramboyang/openjdk-alpine:jre-8u212-timezone-ttf .
# 1.4 版本
docker build -t ramboyang/openjdk-alpine:jre-8u212-timezone .
将本地镜像推送至 Docker Hub 个人公有仓库
docker login
# 1.1 版本
docker push ramboyang/openjdk-alpine:jre-8u212-cert-timezone-ttf
# 1.2 版本
docker push ramboyang/openjdk-alpine:jre-8u212-timezone-ttf-bash
# 1.3 版本
docker push ramboyang/openjdk-alpine:jre-8u212-timezone-ttf
# 1.4 版本
docker push ramboyang/openjdk-alpine:jre-8u212-timezone
采用此镜像为基础镜像构建 JAVA 应用
# From 基础镜像(包含了东八区的设置、ttf-dejavu绘制验证码组件)
FROM ramboyang/openjdk-alpine:jre-8u212-timezone-ttf
# 定义镜像创建者
LABEL maintainer=Rambo@xx.com
# 前端界面路径
# RUN mkdir -p /opt/java/front/sample-web
# 后端程序路径
WORKDIR /opt/java/spring-boot-sample
COPY ./*.jar ./spring-boot-sample.jar
EXPOSE 8888
ENTRYPOINT ["java", "-jar", "./spring-boot-sample.jar"]
解决方案二
点击此处获取所需的非 alpine 内核的 openjdk
非 alpine 内核的 openjdk 一般都存在本地时间文件夹和时区文件,采用以下 Dockerfile 即可完成容器内本地时间和时区的设置
# From 基础镜像
FROM openjdk:8u275-jre-slim
# 定义镜像创建者
LABEL maintainer=Rambo@xx.com
# 自定义环境变量
ENV TZ=Asia/Shanghai
# 设置本地系统时间和时区
RUN ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && echo ${TZ} > /etc/timezone
# 前端界面路径
# RUN mkdir -p /opt/java/front/sample-web
# 后端程序路径
WORKDIR /opt/java/spring-boot-sample
COPY ./*.jar ./spring-boot-sample.jar
EXPOSE 8888
ENTRYPOINT ["java", "-jar", "./spring-boot-sample.jar"]
解决方案三
Dockerfile 文件中设置 JAVA 应用读取的时间配置文件
# From 基础镜像
FROM openjdk:8-jre-alpine3.9
# 定义镜像创建者
LABEL maintainer=Rambo@xx.com
# 设置本地系统时间和时区
RUN Asia/Shanghai > /etc/timezone
# 前端界面路径
# RUN mkdir -p /opt/java/front/sample-web
# 后端程序路径
WORKDIR /opt/java/spring-boot-sample
COPY ./*.jar ./spring-boot-sample.jar
EXPOSE 8888
ENTRYPOINT ["java", "-jar", "./spring-boot-sample.jar"]
将镜像启动设置容器共享宿主机的时区
docker run --name spring-boot-sample -v /etc/localtime:/etc/localtime:ro -p 8888:8888 镜像ID
1
解决方案四
从一台有时间文件夹的 Linux 服务器复制出 zoneinfo 文件夹到 Dockerfile 所在构建文件夹
cp -r /usr/share/zoneinfo Dockerfile所在构建文件夹目录
1
Dockerfile 构建镜像时将本地 zoneinfo 文件夹复制到 Alpine Linux 系统中的固定目录
# From 基础镜像
FROM openjdk:8-jre-alpine3.9
# 定义镜像创建者
LABEL maintainer=Rambo@xx.com
# 复制时间文件夹到指定 Alpine 目录
COPY zoneinfo /usr/share/zoneinfo/
# 自定义环境变量
ENV TZ=Asia/Shanghai
# 设置本地系统时间和时区
RUN ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && echo ${TZ} > /etc/timezone
# 前端界面路径
# RUN mkdir -p /opt/java/front/sample-web
# 后端程序路径
WORKDIR /opt/java/spring-boot-sample
COPY ./*.jar ./spring-boot-sample.jar
EXPOSE 8888
ENTRYPOINT ["java", "-jar", "./spring-boot-sample.jar"]
解决方案五(不推荐)
在构建镜像过程中安装时区组件(受网络的影响,会导致构建镜像变慢或者失败)
# From 基础镜像
FROM openjdk:8-jre-alpine3.9
# 定义镜像创建者
LABEL maintainer=Rambo@xx.com
#安装时区组件
RUN apk add -U tzdata
# 自定义环境变量
ENV TZ=Asia/Shanghai
# 设置本地系统时间和时区
RUN ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && echo ${TZ} > /etc/timezone
# 前端界面路径
# RUN mkdir -p /opt/java/front/sample-web
# 后端程序路径
WORKDIR /opt/java/spring-boot-sample
COPY ./*.jar ./spring-boot-sample.jar
EXPOSE 8888
ENTRYPOINT ["java", "-jar", "./spring-boot-sample.jar"]
K8S容器时区问题解决
方法一
在制作容器镜像时候,将时区问题解决
RUN rm -f /etc/localtime && ln -sv /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" > /etc/timezone
方法二
采用hostpath方式将主机时区挂载至容器内部
volumeMounts:
- mountPath: /etc/localtime
name: time_name
volumes:
- hostPath:
path: /usr/share/zoneinfo/Asia/Shanghai
type: ''
name: time_name
nginx伪静态try_files和rewrite
location ^~ /location {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
完整的解释是:try_files 去尝试到网站目录读取用户访问的文件,如果第一个变量存在,就直接返回;不存在继续读取第二个变量,如果存在,直接返回;不存在直接跳转到第三个参数上。变量就是每个uri资源。并不改变请求路径。 $uri表示文件资源, $uri/表示目录资源。
location ^~ /location {
root /usr/share/nginx/html;
index index.html index.htm;
rewrite http://www.baidu.com break;
}
请求location,跳转至百度,改变请求路径。break表示不再向下匹配。
hpa自动扩容缩
kubectl autoscale deployment 名称 --cpu-percent=80 --min=3 --max=6 -n 命名空间
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: gsvauth-v1
namespace: istio-system
spec:
maxReplicas: 6
minReplicas: 1
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: gsvauth-v1
targetCPUUtilizationPercentage: 80
secret模板
生成系统变量的模板
apiVersion: v1
kind: Secret
metadata:
name: gitlogin
namespace: template
type: Opaque
data:
username: xxxxx ## 直接base64直接转码后挂进来 使用"echo 字符串 | base64加密,使用echo 字符串 | base64 -d解密"
password: xxxxx ## 此处密码被挂载到git服务中,其中含有@字符,必须将“@”转义成“%40”,然后进行base64转换成密码
---
apiVersion: batch/v1
kind: Job
metadata:
name: git
namespace: template
spec:
template:
metadata:
name: git
labels:
app: git
version: v1
spec:
containers:
- name: git
image: bitnami/git:latest
workingDir: /tmp
command:
- "sh"
- "-c"
- git clone -b dev https://${username}:${password}@xxx.git && echo "job done!"
#- env
env:
- name: username
valueFrom:
secretKeyRef:
name: gitlogin
key: username
- name: password
valueFrom:
secretKeyRef:
name: gitlogin
key: password
restartPolicy: OnFailure
