En la edición 2017 de la Global Azure Bootcamp servidor hospedó en Azure un servidor de TetriNET dockerizado disponible para todo el que quisiera conectarse.
Este año vamos a mantenerlo pero con una vuelta de tuerca adicional: estará hospedado en Azure Kubernetes Service. Con el contendor del servidor ya desarrollado, la adaptación a AKS ha sido trivial y meramente basada en construir las declaraciones YAML necesarias para Kubernetes. Aún así creo que es lo suficientemente relevante para mencionarlo en un artículo.
Para este propósito he creado dos YAML, uno que crea el Service y el Secret donde irán las password de administrador del servicio y un segundo con el Deployment.
El motivo de esta división es que, como vamos a ver, el tipo del Service es LoadBalancer, ya que AKS se encargará de gestionarlo. La creación del balanceador de carga y sus reglas le lleva a Azure unos pocos minutos y nuestro Deployment se quejará amargamente y dará error si su Service no está en funcionamiento previamente. A diferencia de las plantillas JSON de ARM, no parece que haya forma de especificar dependencias dentro de un mismo YAML de Kubernetes, por lo que no queda más remedio que dividir la implementación en dos archivos y lanzarlos secuencialmente tras un tiempo prudencial. Agradeceré que me dejéis ideas y/o comentarios a este respecto.
Antes de continuar, os recuerdo que vamos usar la imagen Docker que ya construimos para la Global Azure Bootcamp 2017.
tetrinetx-aks-1.yaml
Lo primero es definir el Service, que será la IP estable y conocida mediante la cual nos podremos conectar a nuestro servidor. Una vez más, los Pods que se encuentran bajo un Deployment son susceptibles de cambiar dinámicamente y su red agnóstica a nosotros.
apiVersion: v1
kind: Service
metadata:
name: tetrinetx
labels:
app: tetrinetx
spec:
ports:
- name: http
protocol: TCP
port: 80
- name: tetrinet
protocol: TCP
port: 31457
- name: tetrispec
protocol: TCP
port: 31458
selector:
app: tetrinetx
type: LoadBalancer
loadBalancerIP: 52.174.54.111
clusterIP: 10.0.1.1 # Default range: 10.0.0.0/16
---
apiVersion: v1
kind: Secret
metadata:
name: tetrinetx
labels:
app: tetrinetx
type: Opaque
data:
op-pwd: cDRzc3cwcmQK # p4ssw0rd in base64
spec-pwd: cDRzc3cwcmQK # p4ssw0rd in base64
Ningun misterio aquí: definimos los labels del selector y los puertos disponbiles para la conexión. Como siempre, ClusterIP será la IP interna estable de nuestro servidor, mientras que loadBalancerIP será la pública del Azure Load Balancer, atributo que por cierto es opcional, siendo este un matiz importante.
Si la definición especificamos type como LoadBalancer, pero no definimos loadBalancerIP, el servicio se creará con una IP aleatoria que Azure le asigne al balanceador de carga.
En cambio, si hemos definido loadBalancerIP, Azure nos asignará dicha IP siempre que el recurso de Public IP que la contenga haya sido creado previamente y se encuentre en el mismo grupo de recursos que el cluster de AKS. Así que no, no podemos pedirle a Azure la dirección IP pública que queramos.
En cuanto al Secret sólo mencionar que las contraseñas se especifican en base64.
tetrinetx-aks-2.yaml
Ahora hacemos la definicion del Deployment, que quedaría tal que así:
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2, before 1.8.0 use apps/v1beta1
kind: Deployment
metadata:
name: tetrinetx
labels:
app: tetrinetx
spec:
replicas: 1
selector:
matchLabels:
app: tetrinetx
strategy:
type: Recreate
template:
metadata:
labels:
app: tetrinetx
spec:
hostname: tetrinet
subdomain: azurebootcamp
containers:
- image: cmilanf/tetrinetx
imagePullPolicy: Always
name: tetrinetx
env:
- name: OP_PASSWORD
valueFrom:
secretKeyRef:
name: tetrinetx
key: op-pwd
- name: SPEC_PASSWORD
valueFrom:
secretKeyRef:
name: tetrinetx
key: spec-pwd
ports:
- containerPort: 80
name: http
- containerPort: 31457
name: tetrinetx
- containerPort: 31458
name: tetrispec
Notas sobre esta declaración:
- Tetrinetx nunca se diseñó para escalar, así que el número de réplicas por defecto es 1. ¿Significa esto que no podemos escalarlo a -por ejemplo- 200 instancias? Para nada, desde luego que podemos, pero serán totalmente independientes y los jugadores acabarán en una u otra sin control alguno; algo que no es muy deseable.
- Bajo spec tenemos la plantilla de definición del Pod que se va a crear a partir de este Deployment.
- Las líneas 19 y 20 son para la especificación de un hostname, que era un requisito de Tetrinetx que ya teníamos que afrontar desplegándolo en bruto mediante Docker.
- La línea 22 indica la imagen Docker que vamos a utilizar en los Pods y como no estamos especificando nada especial, por defecto buscará en el repositorio público oficial de Docker, es decir descargará esta imagen.
- La línea 23 especifica que siempre iremos al repositorio pertinente a descargar la imagen ante una reimplementación del Pod. Es muy útil para testing y desarrollo, pero se debe manejar con cuidado en entornos productivos, en especial si nuestras imágenes son grandes.
- Especificamos variables y puertos, al igual que ya hacíamos con Docker.
Resultado
Es el siguiente:
C:\>kubectl get secrets -l app=tetrinetx
NAME TYPE DATA AGE
tetrinetx Opaque 2 9d
C:\>kubectl get service -o wide -l app=tetrinetx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
tetrinetx LoadBalancer 10.0.1.1 52.174.xxx.xxx 80:31888/TCP,31457:31255/TCP,31458:30115/TCP 9d app=tetrinetx
C:\>kubectl get deployment -o wide -l app=tetrinetx
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
tetrinetx 1 1 1 1 9d tetrinetx cmilanf/tetrinetx app=tetrinetx
C:\>kubectl get pods -o wide -l app=tetrinetx
NAME READY STATUS RESTARTS AGE IP NODE
tetrinetx-8559b876c7-8x9d7 1/1 Running 0 9d 10.244.xxx.xxx aks-nodepool1-393643750-0
Podemos dirigirnos a http://tetrinet.azurebootcamp.es o http://tetrinet.calnus.com, descargar nuestro cliente y apuntarlo a tetrinet.azurebootcamp.es o tetrinet.calnus.com.
Happy tetrit!