Kubernetes для бедных. Запускаем приложение

После того как мы настроили доступ по HTTP к приложениям в кластере Kubernetes, разместим в кластере собственно приложение.

Приложение должно поставляться в образе контейнера и уметь отвечать на запросы HTTP. Соберём контейнер Docker на базе Hugo. Для работы с собственными образами нужно быть зарегистрированным на одном из Docker Registry. Самое простое - использовать официальный Docker Hub. Образ, с которым мы будем работать, собирается так (потребуется Docker >= 17.05):

mkdir /tmp/hugo
cd /tmp/hugo

cat <<EOF > Dockerfile
FROM alpine AS base

RUN wget -O- https://github.com/gohugoio/hugo/releases/download/v0.49/hugo_0.49_Linux-64bit.tar.gz | tar xz
RUN /hugo new site /site
RUN wget -O- https://github.com/hauke96/hugo-theme-hamburg/archive/v0.4.4.tar.gz | tar xzC /site/themes
RUN echo 'theme = "hugo-theme-hamburg-0.4.4"' >> /site/config.toml

FROM alpine

COPY --from=base /hugo /
COPY --from=base /site /site
WORKDIR /site

EXPOSE 1313
ENTRYPOINT ["/hugo"]
CMD ["server", "--bind", "0.0.0.0"]
EOF

docker build -t alxrem/hugoexample .
docker push alxrem/hugoexample

Если захочется пересобрать образ самостоятельно, нужно будет заменить alxrem на название своей учётной записи.

После того, как образ собран, можно публиковать приложение в Kubernetes.

Нам потребуется создать три ресурса - Deployment, Service и Ingress. Выделим отдельный namespace:

kubectl create ns blogexample

При помощи Deployment разместим в кластере Kubernetes два экземпляра приложения: на случай отказа ноды и для более стабильного обновления:

cat <<EOF | kubectl -n blogexample create -f-
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: blogexample
  name: blogexample
spec:
  replicas: 2
  selector:
    matchLabels:
      app: blogexample
  template:
    metadata:
      labels:
        app: blogexample
    spec:
      containers:
      - name: www
        image: alxrem/hugoexample
        imagePullPolicy: Always
        ports:
        - containerPort: 1313
          protocol: TCP
EOF

Создадим Service для определения доступа к контейнеру:

cat <<EOF | kubectl -n blogexample create -f-
apiVersion: v1
kind: Service
metadata:
  labels:
    app: blogexample
  name: blogexample
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 1313
  selector:
    app: blogexample
  type: ClusterIP
EOF

Теперь внутри кластера приложение будет доступно по адресу blogexample.blogexample.

Для доступа снаружи понадобится завести в DNS запись A, указывающую на внешние адреса нодов, которые мы помечали меткой remizov.org/ingress=true.

Для управления доступом создадим Ingress. Определим, что запросы к хосту blogexample.remizov.org будут отправляться на порт 80 сервиса blogexample. При этом выпишем для адреса blogexample.remizov.org сертификат от letsencrypt.org. В отличие от прошлого примера будем использовать боевой центр сертификации letsencrypt-v01:

cat <<EOF | kubectl -n blogexample create -f-
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    certmanager.k8s.io/cluster-issuer: letsencrypt-v01
    kubernetes.io/ingress.class: nginx
  name: blogexample
spec:
  rules:
  - host: blogexample.remizov.org
    http:
      paths:
      - backend:
          serviceName: blogexample
          servicePort: 80
        path: /
  tls:
  - hosts:
    - blogexample.remizov.org
    secretName: blogexample-tls
EOF

В итоге по адресу https://blogexample.remizov.org доступен блог с валидным сертификатом SSL

Пример блога с валидным сертификатом