Kubernetes. Deployment. Разворачиваем приложение из заранее созданного Docker-образа
Описание
Deployment - деплой в кластер
Это такая расширенная версия ReplicaSet.
Переходим к следующей абстракции Kubernetes - Deployment. Они управляют наборами replicaset для непрерывного обновления подов. Покажу на примере, о чем идет речь. Допустим, у вас вышло обновление приложения в новом контейнере. Вам нужно не останавливая сервис выкатить обновление в прод. Если вы измените версию контейнера в шаблоне replicaset, автоматически он у вас не обновится. Да, если pod со старой версией контейнера упадет, новый будет создан уже с новой версией. Но все работающие поды останутся на старой версии.
Deployment как раз и нужен для управления репликасетами, задавая им стратегию обновления. У вас вышла новая версия контейнера, вы заменяете в текущем deployment версию контейнера и он по заранее настроенным правилам начинает перезапуск репликасетов и соответственно подов в них.
Настройка
Пример 1. Nginx
Деплоймент, который создаст 2 реплики пода nginx02
nano ./deployment-nginx.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: nginx02 spec: replicas: 2 selector: matchLabels: app: nginx02 strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 1 type: RollingUpdate template: metadata: labels: app: nginx02 spec: containers: - image: nginx:1.16 name: nginx02 ports: - containerPort: 80
kubectl apply -f deployment-nginx.yaml
kubectl get deployment -o wide
... NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR nginx02 2/2 2 2 10m nginx02 nginx:1.16 app=nginx02 ...
kubectl get pod -o wide
... NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx02-59dc87447c-9nrkj 1/1 Running 0 10m 172.16.213.141 kub-node-1 <none> <none> nginx02-59dc87447c-d7vf5 1/1 Running 0 10m 172.16.213.77 kub-node-2 <none> <none> ...
До создания сервиса можно временно пробросить порт в деплоймент и проверить что nginx из контейнера отвечает по порту 80 на ноде, на которой запустили временный проброс
kubectl port-forward --address 0.0.0.0 deployments/nginx02 80:80
На любой Linux машине:
curl "http://192.168.199.184/"
Если nginx отвечает - нажимаем Ctrl+C в консоли где запускали kubectl port-forward --address 0.0.0.0 deployments/nginx02 80:80
Тепрь создадим service чтобы перманентно пробросить порты в deployment nginx02
nano ./service.yaml
apiVersion: v1 kind: Service metadata: name: nginx02 spec: ports: - port: 80 targetPort: 80 selector: app: nginx02 type: NodePort
kubectl apply -f service.yaml
kubectl get svc -o wide
... NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d12h <none> nginx02 NodePort 10.101.196.165 <none> 80:32032/TCP 6m41s app=nginx02 ...
Nginx Pod теперь развернут в кластере Kubernetes и доступен через Интернет
Доступ из веб-браузера по порту 32032 (МОЖНО ПРОЙТИ ПО IP ЛЮБОЙ НОДЫ КЛАСТЕРА KUBERNETES)
http://192.168.199.184:32032/
Изменить количество реплик deployment
kubectl scale deployment nginx02 --replicas=3
Удалить deployment
kubectl delete deployment nginx02
Пример 2. Go App
Создание кастомного образа контейнера "goapp"
Приложение на GO будет работать на порте 8080 и отдавать резутьтат через HTTP
mkdir ./goapp
cd ./goapp
nano ./main.go
package main
import (
"fmt"
"log"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Dockerfile для создания образа контейнера с приложением
Each instruction creates one layer:
FROM creates a layer from the golang:1.10 Docker image.
COPY adds files from your Docker client’s current directory.
RUN builds your application with make.
CMD specifies what command to run within the container.
nano Dockerfile
FROM golang:1.10 WORKDIR /app COPY . . RUN go build -o main . CMD ["/app/main"]
Собираем ораз
docker build . -t goapp:v0.1
docker images
А теперь поправим Докерфайл на такой и соберем с тэгом v0.2
nano Dockerfile
# Создаем временный Докер-образ и называем его builder FROM golang:1.10 AS builder WORKDIR /app COPY . . # Компилируем приложение специально для дальнейшего запуска в Alpine RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . # Создаем финальный Докер-образ на базе легковесного alpine FROM alpine:latest WORKDIR /root/ # Копируем собранное приложение из образа biulder COPY --from=builder ./app . EXPOSE 8080 CMD ["./app"]
docker build . -t goapp:v0.2
docker images
... REPOSITORY TAG IMAGE ID CREATED SIZE goapp v0.2 c3b7c4cb4a1f 30 minutes ago 12.1MB <none> <none> 698ac8b17e93 30 minutes ago 782MB goapp v0.1 c73b00641856 42 minutes ago 767MB ...
Второй Докерфайл дал нам образ, который в 50 раз меньше весит. Как это вышло?
В первом Докерфайле мы сказали Докеру собрать образ, в котором будет Golang версии 1.10, а также наше приложение. Но Go - это компилируемый язык, его артефакт - исполняемый бинарный файл, который не обязательно запускать, имея рядышком достаточно тяжеловесный компилятор Golang, он нам нужен лишь при сборке приложения.
Поэтому во втором Докерфайле мы использовали Golang и собрали бинарь в первом этапе, а во втором этапе мы использовали легковесный Alpine, чтобы запускать наше приложение
А еще мы только что сделали первый веб-сервис в Докере:
docker run -d -p 8080:8080 go:v0.2
docker ps -a
... CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ee2eacdb1a6f goapp:v0.2 "./app" 29 minutes ago Up 15 minutes 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp reverent_mclean ...
netstat -luptn
... tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 7505/docker-proxy ...
curl "http://127.0.0.1:8080/gogogo"
... Hi there, I love gogogo! ...
Заливаем образ "goapp" на hub.docker.com
Деплоймент, который создаст 2 реплики пода nginx02
nano ./deployment-nginx.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: nginx02 spec: replicas: 2 selector: matchLabels: app: nginx02 strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 1 type: RollingUpdate template: metadata: labels: app: nginx02 spec: containers: - image: nginx:1.16 name: nginx02 ports: - containerPort: 80
kubectl apply -f deployment-nginx.yaml
kubectl get deployment -o wide
... NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR nginx02 2/2 2 2 10m nginx02 nginx:1.16 app=nginx02 ...
kubectl get pod -o wide
... NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx02-59dc87447c-9nrkj 1/1 Running 0 10m 172.16.213.141 kub-node-1 <none> <none> nginx02-59dc87447c-d7vf5 1/1 Running 0 10m 172.16.213.77 kub-node-2 <none> <none> ...
До создания сервиса можно временно пробросить порт в деплоймент и проверить что nginx из контейнера отвечает по порту 80 на ноде, на которой запустили временный проброс
kubectl port-forward --address 0.0.0.0 deployments/nginx02 80:80
На любой Linux машине:
curl "http://192.168.199.184/"
Если nginx отвечает - нажимаем Ctrl+C в консоли где запускали kubectl port-forward --address 0.0.0.0 deployments/nginx02 80:80
Тепрь создадим service чтобы перманентно пробросить порты в deployment nginx02
nano ./service.yaml
apiVersion: v1 kind: Service metadata: name: nginx02 spec: ports: - port: 80 targetPort: 80 selector: app: nginx02 type: NodePort
kubectl apply -f service.yaml
kubectl get svc -o wide
... NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d12h <none> nginx02 NodePort 10.101.196.165 <none> 80:32032/TCP 6m41s app=nginx02 ...
Nginx Pod теперь развернут в кластере Kubernetes и доступен через Интернет
Доступ из веб-браузера по порту 32032 (МОЖНО ПРОЙТИ ПО IP ЛЮБОЙ НОДЫ КЛАСТЕРА KUBERNETES)
http://192.168.199.184:32032/
Изменить количество реплик deployment
kubectl scale deployment nginx02 --replicas=3
Удалить deployment
kubectl delete deployment nginx02