
Это небольшая заметка на тему как запаковать vue.js приложение в Dockerfile и потом его запустить в контейнере в kubernetes’e.
Что делает
Я написал небольшую программку, которая генерирует номер свободного NodePort. Собственно ничего особо полезного она не делает, но можно не парится с поиском порта, ну и так для интереса посмотреть, как такое можно делать.
Начали
Весь проект состоит из друх частей — frontend и server. Фронтент спрашивает nodePort у сервера, а серверная часть через kubernetes api находит какой-нибудь свободный.
Собственно чтобы это всё работало в докере, надо вынести некоторые переменные из приложения, такие как адрес kubernetes api, порт, токен, итд.
Выглядит это так:
k8s-nodeport-gen/server/index.js: var k8sInfo = { url: process.env.K8SURL, port: process.env.K8SPORT, timeout: process.env.K8STIMEOUT || '30', respath: process.env.RESSPATH || '/api/v1/services', token: process.env.K8STOKEN, nodePortStart: process.env.K8SPORTSTART || '30000', nodePortEnd: process.env.K8SPORTEND || '32000' } app.listen(process.env.PORT || 8081)
Cкажем, что всё протестировали и наше приложение работает.
Создаём докер образ
Те, кто работал с vue.js знает, что там куча всяких файлов, для чего они все нужны я не знаю, но видать нужны. Но благодаря тому, что есть такая вещь как vue-cli, всё можно довольно просто упаковать. Теперь всё пакуем:
npm run build
После этого у нас появится папка «dist» и файл «index.html» в «k8s-nodeport-gen/client». И для работы нам нужны только они. То есть по идее чтобы работал фронтенд надо какой-нибудь http сервер. Но в данном случае есть ещё и бэкенд, который тоже должен работать. По этому в моём случае как http сервер будет работать node.js express.
Файлы будут позде лежать в папке k8s-nodeport-gen/public. Для этого добавляем опцию в server/index.js
app.use(express.static(__dirname + '/public'))
Теперь когда разобрали с файлами можно создать Dockerfile. Нам из фронтенда надо только создать файлы для папки «dist». Для этого мы воспользуемся такой новомодной штукой, как multistage build.
FROM node:10-alpine AS base COPY client /portgen/client COPY server /portgen/server WORKDIR /portgen RUN cd client && npm i && npm run build FROM node:10-alpine WORKDIR /portgen COPY server/index.js /portgen/index.js COPY server/package.json /portgen/package.json COPY --from=base /portgen/client/dist ./public RUN npm i CMD ["/usr/local/bin/node", "/portgen/index.js"]
То есть в первом контейнере запускаем «npm run build», а во втором контейнере копируем файлы из «dist» в «public». В конце у нас получается образ в 95мб.
Теперь у нас есть docker образ, который я уже залил на hub.docker.com.
Запуск
Теперь мы хотим запустить этот образ в kubernetes’e, к тому же нам нужен токен, который может через kubernetes api видеть какие порты уже используются.
Для этого надо надо создать сервесный аккаунт, роль(можно использовать уже существующую) и rolebinding (не знаю как правильно перевести).
У меня в кластере уже есть кластерная роль «view»
ceku@ceku1> kubectl describe clusterrole view Name: view Labels: kubernetes.io/bootstrapping=rbac-defaults Annotations: rbac.authorization.kubernetes.io/autoupdate=true PolicyRule: Resources Non-Resource URLs Resource Names Verbs --------- ----------------- -------------- ----- bindings [] [] [get list watch] configmaps [] [] [get list watch] endpoints [] [] [get list watch] events [] [] [get list watch] limitranges [] [] [get list watch] namespaces [] [] [get list watch] namespaces/status [] [] [get list watch] persistentvolumeclaims [] [] [get list watch] pods [] [] [get list watch] pods/log [] [] [get list watch] pods/status [] [] [get list watch] replicationcontrollers [] [] [get list watch] replicationcontrollers/scale [] [] [get list watch] replicationcontrollers/status [] [] [get list watch] resourcequotas [] [] [get list watch] resourcequotas/status [] [] [get list watch] serviceaccounts [] [] [get list watch] services [] [] [get list watch] daemonsets.apps [] [] [get list watch] deployments.apps [] [] [get list watch] deployments.apps/scale [] [] [get list watch] replicasets.apps [] [] [get list watch] replicasets.apps/scale [] [] [get list watch] statefulsets.apps [] [] [get list watch] horizontalpodautoscalers.autoscaling [] [] [get list watch] cronjobs.batch [] [] [get list watch] jobs.batch [] [] [get list watch] daemonsets.extensions [] [] [get list watch] deployments.extensions [] [] [get list watch] deployments.extensions/scale [] [] [get list watch] ingresses.extensions [] [] [get list watch] networkpolicies.extensions [] [] [get list watch] replicasets.extensions [] [] [get list watch] replicasets.extensions/scale [] [] [get list watch] replicationcontrollers.extensions/scale [] [] [get list watch] networkpolicies.networking.k8s.io [] [] [get list watch] poddisruptionbudgets.policy [] [] [get list watch]
Теперь создадим аккаунт и rolebinding
account_portng.yml:
apiVersion: v1 kind: ServiceAccount metadata: name: portng-service-get namespace: internal labels: k8s-app: portng-service-get kubernetes.io/cluster-service: "true"
rolebindng_portng.yml:
kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: namespace: internal name: view labels: k8s-app: portng-service-get kubernetes.io/cluster-service: "true" addonmanager.kubernetes.io/mode: Reconcile subjects: - kind: ServiceAccount name: portng-service-get namespace: kube-system apiGroup: "" roleRef: kind: ClusterRole name: view apiGroup: ""
Теперь у нас есть аккаунт, а у него есть токен. Его название написано в аккаунте:
ceku@ceku1 /a/r/aditointernprod.aditosoftware.local> kubectl get serviceaccount portng-service-get -n internal -o yaml apiVersion: v1 kind: ServiceAccount metadata: creationTimestamp: 2018-08-02T07:31:54Z labels: k8s-app: portng-service-get kubernetes.io/cluster-service: "true" name: portng-service-get namespace: internal resourceVersion: "7270593" selfLink: /api/v1/namespaces/internal/serviceaccounts/portng-service-get uid: 2153dfa0-9626-11e8-aaa3-ac1f6b664c1c secrets: - name: portng-service-get-token-vr5bj
Теперь надо только написать deploy, service, ingress для страницы. Начнём:
deploy_portng.yml
apiVersion: apps/v1beta1 # for versions before 1.6.0 use extensions/v1beta1 kind: Deployment metadata: namespace: internal name: portng.server.local spec: replicas: 1 template: metadata: labels: app: portng.server.local spec: serviceAccountName: portng-service-get containers: - name: portgen image: de1m/k8s-nodeport-gen env: - name: K8SURL value: ceku.server.local - name: K8SPORT value: '6443' - name: K8STIMEOUT value: '30' - name: RESSPATH value: '/api/v1/services' - name: K8SPORTSTART value: '30000' - name: K8SPORTEND value: '32000' - name: PORT value: '8080' args: - /bin/sh - -c - export K8STOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) && node /portgen/index.js
Здесь надо обратить внимание на две вещи, это «serviceAccountName: portng-service-get» и токен для kubernetes’a, точнее говоря то, как я его добавил.
Теперь напишем сервис:
svc_portng.yml
apiVersion: v1 kind: Service metadata: name: portng-server-local namespace: internal spec: ports: - name: http port: 8080 targetPort: 8080 selector: app: portng.server.local
И ingress, для него у вас должен быть установлен ingress controller
ingress_portng.yaml:
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: portng.aditosoftware.local namespace: internal annotations: kubernetes.io/ingress.class: "internal" spec: rules: - host: portng.server.local http: paths: - path: / backend: serviceName: portng-server-local servicePort: 8080
Всё, осталось только закинуть файлы на сервер и запустить.
Всё это можно запустить и как докер контейнер и даже без него, но часть с аккаунтами в kubernetes’e всё равно придётся проходить.
Ресурсы:
Docker образ на hub.docker.com
Git репозиторий на github.com
Как вы видете, ничего особого в данной статье нету, но для некоторых я думаю будет интересно.
ссылка на оригинал статьи https://habr.com/post/419033/
Добавить комментарий