Сладков Данила Алексеевич sladkkov@yandex.ru
Программа выполенна на языке Java. Программа отражает функционал простого калькулятора
Использованы: Java, JUnit, Swagger, Spring Web.
Java 17 Run app: src/main/java/ru/sladkkov/calculator/CalculatorApplication.java
Swagger Doc: http://localhost:9090/swagger-ui/index.html#/
- Был установлен Docker.
- Был создан аккаунт на Docker Hub.
- В качестве приложения выбрал Rest Calculator написанный на Java.
Создадим Dockerfile
FROM openjdk:8 ADD . /src WORKDIR /src
ARG UID=1001 ARG GID=1001
RUN addgroup --system --gid ${GID} user RUN adduser --system --uid ${UID} --group user RUN chown -R user:user /src
USER userCOPY /target/Calculator-0.0.1-SNAPSHOT.jar Calculator.jar
EXPOSE 8080 ENTRYPOINT ["java","-jar", "Calculator.jar"]
Выполним команды
- docker build -t sladkkov/calculator:1.0.0 -t sladkkov/calculator:1.0.0 . (Создаем image c тэгом 1.0.0 и названием sladkkov/calculator)
-docker run -ti --rm -p 9090:9090 --name calculator sladkkov/calculator:1.0.0
После этого я проверил, что всё работает. Открыл Docker Desktop и увидел, что размер имейджа 534 Мб, что мне показалось слишком много и второе, что мне не понравилось, то что проект использует готовый jar файл, который был получен до этого.
Создадим новый Dockerfile.
# syntax=docker/dockerfile:1 FROM openjdk:16-alpine3.13
WORKDIR /RestApiCalculator
COPY .mvn/ .mvn COPY mvnw pom.xml ./ RUN ./mvnw dependency:go-offline COPY src ./src
CMD ["./mvnw", "spring-boot:run"]
Также был добавлен файл .dockeringore, в котором папка target
Изучив документацию, пришёл к вот такому варианту, который сам загружает зависимости, и запускает spring-boot:run.
В этом варианте размер составляет 604.06 MB
Здесь я не добавлял нового user’a, потому что это не финальная версия, а с разными базовыми образами приходится каждый раз менять свойства.
Изучив ещё больше статей, видео и документации, пришел к такому варианту, используя multi-stage построение image.
FROM adoptopenjdk/openjdk11:jdk-11.0.5_10-alpine as builder
ADD . /src WORKDIR /src RUN ./mvnw package -DskipTests
ARG UID=1001 ARG GID=1001
RUN addgroup --system --gid ${GID} user && adduser --system --uid ${UID} user --ingroup user RUN chown -R user:user /opt RUN chown -R user:user /src
USER user
FROM alpine:3.10.3 as packager RUN apk --no-cache add openjdk11-jdk openjdk11-jmods ENV JAVA_MINIMAL="/opt/java-minimal" RUN /usr/lib/jvm/java-11-openjdk/bin/jlink
--verbose
--add-modules
java.base,java.naming,java.desktop,java.management,java.security.jgss,java.instrument
--compress 2 --strip-debug --no-header-files --no-man-pages
--output "$JAVA_MINIMAL"FROM alpine:3.10.3 ENV JAVA_HOME=/opt/java-minimal ENV PATH="$PATH:$JAVA_HOME/bin"
COPY --from=packager "$JAVA_HOME" "$JAVA_HOME" COPY --from=builder /src/target/Calculator-0.0.1-SNAPSHOT.jar Calculator.jar EXPOSE 9090 ENTRYPOINT ["java","-jar","/Calculator.jar"]
Здесь мы вручную конфигурируем модули, которые нам нужны для работы приложения.
Начальную версию кода, я нашёл в статье на Хабре. Было миллион ошибок в процессе допиливания под себя. Изучил много видео, статей и документации, о том как всё здесь работает, и достаточно хорошо разобрался.
По этой команде узнаем модули в нашем проекте
jdeps C:\Users\sladk\Downloads\control-work-time-main\RestApiCalculator
Размер image удалось сократить до 81.46 MB. Что меня вполне устроило.
Выполним команды
- docker build -t sladkkov/calculator:1.0.0 -t sladkkov/calculator:1.0.0 . (Создаем image c тэгом 1.0.0 и названием sladkkov/calculator)
[+] Building 101.7s (20/20) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 1.01kB 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 34B 0.0s
=> [internal] load metadata for docker.io/library/alpine:3.10.3 2.6s
=> [internal] load metadata for docker.io/adoptopenjdk/openjdk11:jdk-11.0.5_10-alpine 2.5s
=> [auth] adoptopenjdk/openjdk11:pull token for registry-1.docker.io 0.0s
=> [auth] library/alpine:pull token for registry-1.docker.io 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 7.92kB 0.0s
=> [stage-2 1/3] FROM docker.io/library/alpine:3.10.3@sha256:c19173c5ada610a5989151111163d28a67368362762534d8a8121ce95cf2bd5a 0.0s
=> => resolve docker.io/library/alpine:3.10.3@sha256:c19173c5ada610a5989151111163d28a67368362762534d8a8121ce95cf2bd5a 0.0s
=> CACHED [builder 1/7] FROM docker.io/adoptopenjdk/openjdk11:jdk-11.0.5_10-alpine@sha256:651c0c23656fe6e57c5ae6b9953a8c9bb23ba3f53e55b2c6df6ef243a3d07d8b 0.0s
=> => resolve docker.io/adoptopenjdk/openjdk11:jdk-11.0.5_10-alpine@sha256:651c0c23656fe6e57c5ae6b9953a8c9bb23ba3f53e55b2c6df6ef243a3d07d8b 0.0s
=> [builder 2/7] ADD . /src 0.1s
=> [builder 3/7] WORKDIR /src 0.1s
=> [builder 4/7] RUN ./mvnw package -DskipTests 90.3s
=> [builder 5/7] RUN addgroup --system --gid 1001 user && adduser --system --uid 1001 user --ingroup user 0.7s
=> [builder 6/7] RUN chown -R user:user /opt 6.0s
=> [builder 7/7] RUN chown -R user:user /src 1.4s
=> CACHED [packager 2/3] RUN apk --no-cache add openjdk11-jdk openjdk11-jmods 0.0s
=> CACHED [packager 3/3] RUN /usr/lib/jvm/java-11-openjdk/bin/jlink --verbose --add-modules java.base,java.naming,java.desktop,java.management,java.security.jgss,java.instrument --compress 2 --strip-debug --no-header-files --no-man-pages --output 0.0s
=> CACHED [stage-2 2/3] COPY --from=packager /opt/java-minimal /opt/java-minimal 0.0s
=> [stage-2 3/3] COPY --from=builder /src/target/Calculator-0.0.1-SNAPSHOT.jar Calculator.jar 0.1s
=> exporting to image 0.1s
=> => exporting layers 0.1s
=> => writing image sha256:b0fdb510c783e96e3e5f4f78e42210370b5ef4250df91771d06b997a39515355 0.0s
=> => naming to docker.io/sladkkov/calculator:1.0.0
Посмотрим список image
REPOSITORY TAG IMAGE ID CREATED SIZE
sladkkov/calculator 1.0.0 b0fdb510c783 3 minutes ago 81.5MB
Запустим Docker container из созданного image
-docker run -ti --rm -p 9090:9090 --name calculator sladkkov/calculator:1.0.0
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.6.6)
2022-06-17 03:32:16.273 INFO 1 --- [ main] r.s.calculator.CalculatorApplication : Starting CalculatorApplication v0.0.1-SNAPSHOT using Java 11.0.4 on 35ff9db9b783 with PID 1 (/Calculator.jar started by root in /) 2022-06-17 03:32:16.278 INFO 1 --- [ main] r.s.calculator.CalculatorApplication : No active profile set, falling back to 1 default profile: "default" 2022-06-17 03:32:17.758 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 9090 (http) 2022-06-17 03:32:17.780 INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2022-06-17 03:32:17.781 INFO 1 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.60] 2022-06-17 03:32:17.862 INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2022-06-17 03:32:17.862 INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1477 ms 2022-06-17 03:32:19.339 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 9090 (http) with context path '' 2022-06-17 03:32:19.356 INFO 1 --- [ main] r.s.calculator.CalculatorApplication : Started CalculatorApplication in 3.746 seconds (JVM running for 4.293)
Проверим перейдя по ссылке, http://localhost:9090/swagger-ui/index.html#/Calculation controller/multiply, что приложение работает.
Отправим Docker image в Registry Docker Hub
docker push sladkkov/calculator:1.0.0
PS C:\Users\sladk\Downloads\control-work-time-main\RestApiCalculator> docker push sladkkov/calculator:1.0.0
The push refers to repository [docker.io/sladkkov/calculator]
aa27681dfd45: Pushed
1052af976019: Layer already exists
77cae8ab23bf: Layer already exists
1.0.0: digest: sha256:e9e75476dac11c05cf31175b7342f21ab625e4341564d23b508adb68c5b4f991 size: 952
Откроем web-интерфейс Docker Hub по адресу и найдем загруженный image
docker pull sladkkov/calculator:1.0.0
1.0.0: Pulling from sladkkov/calculator
Digest: sha256:e9e75476dac11c05cf31175b7342f21ab625e4341564d23b508adb68c5b4f991
Status: Image is up to date for sladkkov/calculator:1.0.0
docker.io/sladkkov/calculator:1.0.0
Запуск кластера
PS C:\Users\sladk\Downloads\control-work-time-main\RestApiCalculator> minikube start --embed-certs
😄 minikube v1.25.2 на Microsoft Windows 11 Home Single Language 10.0.22000 Build 22000
✨ Используется драйвер docker на основе существующего профиля
👍 Запускается control plane узел minikube в кластере minikube
🚜 Скачивается базовый образ ...
🔄 Перезагружается существующий docker container для "minikube" ...
❗ This container is having trouble accessing https://k8s.gcr.io
💡 To pull new external images, you may need to configure a proxy: https://minikube.sigs.k8s.io/docs/reference/networking/proxy/
🐳 Подготавливается Kubernetes v1.23.3 на Docker 20.10.12 ...
▪ kubelet.housekeeping-interval=5m
🔎 Компоненты Kubernetes проверяются ...
▪ Используется образ gcr.io/k8s-minikube/storage-provisioner:v5
🌟 Включенные дополнения: storage-provisioner, default-storageclass
🏄 Готово! kubectl настроен для использования кластера "minikube" и "default" пространства имён по умолчанию
Просмотрим статус кластера
PS C:\Users\sladk\Downloads\control-work-time-main\RestApiCalculator> minikube status
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
Посмотрим файл конфига kubeconfig
kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
extensions:
- extension:
last-update: Fri, 17 Jun 2022 07:45:32 +04
provider: minikube.sigs.k8s.io
version: v1.25.2
name: cluster_info
server: https://127.0.0.1:59245
name: minikube
contexts:
- context:
cluster: minikube
extensions:
- extension:
last-update: Fri, 17 Jun 2022 07:45:32 +04
provider: minikube.sigs.k8s.io
version: v1.25.2
name: context_info
namespace: default
user: minikube
name: minikube
current-context: minikube
kind: Config
preferences: {}
users:
- name: minikube
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
Проверка подключения к кластеру.
kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:59245
CoreDNS is running at https://127.0.0.1:59245/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
Создадим pod manifest
apiVersion: v1
kind: Pod
metadata:
name: calculator
labels:
env: test
spec:
containers:
- name: calculator
image: sladkkov/calculator:1.0.0
imagePullPolicy: IfNotPresent
Применим manifest. Манифест ранее уже был создан
PS C:\Users\sladk\Downloads\control-work-time-main\RestApiCalculator> kubectl apply -f pod.yaml -n default
pod/calculator unchanged
Проверим установку манифеста
PS C:\Users\sladk\Downloads\control-work-time-main\RestApiCalculator> kubectl describe pod calculator -n default
Name: calculator
Namespace: default
Priority: 0
Node: minikube/192.168.49.2
Start Time: Fri, 17 Jun 2022 06:40:05 +0400
Labels: env=test
Annotations: <none>
Status: Running
IP: 172.17.0.2
IPs:
IP: 172.17.0.2
Containers:
calculator:
Container ID: docker://8389d4f5f2a6032c584e3fad026da80b8af49f9c1314affc0a00a4e1628bde96
Image: sladkkov/calculator:1.0.0
Image ID: docker-pullable://sladkkov/calculator@sha256:d5fbbf62745a42c04b3b2008465bd835fcc44af54dbff369b1584feaa0d692a1
Port: <none>
Host Port: <none>
State: Running
Started: Fri, 17 Jun 2022 07:45:36 +0400
Last State: Terminated
Reason: Error
Exit Code: 143
Started: Fri, 17 Jun 2022 06:40:06 +0400
Finished: Fri, 17 Jun 2022 06:42:14 +0400
Ready: True
Restart Count: 1
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-2j4fp (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-2j4fp:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 70m default-scheduler Successfully assigned default/calculator to minikube
Normal Pulled 70m kubelet Container image "sladkkov/calculator:1.0.0" already present on machine
Normal Created 70m kubelet Created container calculator
Normal Started 70m kubelet Started container calculator
Normal SandboxChanged 5m23s kubelet Pod sandbox changed, it will be killed and re-created.
Normal Pulled 5m18s kubelet Container image "sladkkov/calculator:1.0.0" already present on machine
Normal Created 5m18s kubelet Created container calculator
Normal Started 5m18s kubelet Started container calculator
PS C:\Users\sladk\Downloads\control-work-time-main\RestApiCalculator>
Пробросим наружу порт web-приложения.
port-forward pods/calculator 9090:9090
Forwarding from 127.0.0.1:9090 -> 9090
Forwarding from [::1]:9090 -> 9090
Handling connection for 9090
Handling connection for 9090
Handling connection for 9090
Удалим Pod с web-приложением
PS C:\Users\sladk\Downloads\control-work-time-main\RestApiCalculator> kubectl delete po calculator
pod "calculator" deleted
Остановим minikube
PS C:\Users\sladk\Downloads\control-work-time-main\RestApiCalculator> minikube stop ✋ Узел "minikube" останавливается ... 🛑 Выключается "minikube" через SSH ... 🛑 Остановлено узлов: 1.