Deni Bertović
https://github.com/denibertovic
https://twitter.com/denibertovic
Software Engineer
@
"Kubernetes is an open-source system for automating deployment,
scaling, and management of containerized applications."
"It groups containers that make up an application into logical
units for easy management and discovery. Kubernetes builds
upon 15 years of experience of running production workloads
at Google, combined with best-of-breed ideas and
practices from the community."
https://github.com/fpco/terraform-aws-foundation
module "vpc" {
source = "fpco/foundation/aws//modules/vpc"
version = "0.7.5"
region = "${var.region}"
cidr = "${var.vpc_cidr}"
name_prefix = "${var.name}"
extra_tags = "${merge(var.extra_tags,
map("kubernetes.io/cluster/${var.kubernetes_cluster_name}", "shared"))}"
}
module "kube-public-subnets" {
source = "fpco/foundation/aws//modules/subnets"
version = "0.7.5"
azs = "${var.aws_availability_zones}"
vpc_id = "${module.vpc.vpc_id}"
name_prefix = "${var.name}-kube-public"
cidr_blocks = "${var.kube_public_subnet_cidrs}"
extra_tags = "${merge(var.extra_tags,
map("kubernetes.io/cluster/${var.kubernetes_cluster_name}", "shared"),
map("kubernetes.io/role/elb", "1"))}"
}
kops create cluster \
--authentication=rbac \
--cloud=aws \
--kubernetes-version=${KUBERNETES_VERSION} \
--networking="flannel" \
--master-size=t2.small \
--master-zones=us-east-1a,us-east-1b,us-east-1c \
--network-cidr=${VPC_CIDR} \
--node-count=${NODE_COUNT} \
--node-size=${NODE_SIZE} \
--ssh-public-key=${SSH_PUBLIC_KEY} \
--zones=us-east-1a,us-east-1b,us-east-1c \
--vpc=${VPC_ID} \
--node-volume-size=${NODE_VOLUME_SIZE} \
--state=s3://${CLUSTER_NAME} \
--name=${CLUSTER_NAME}
kops edit cluster --name ${CLUSTER_NAME} \
--state s3://${CLUSTER_NAME}
metadata:
...
name: ${CLUSTER_NAME}
spec:
cloudProvider: aws
networkCIDR: ....
networkID: ....
nonMasqueradeCIDR: 100.64.0.0/10
subnets:
- cidr: 172.20.32.0/19
name: us-east-1b
type: Public
zone: us-east-1b
id: ....
kops update cluster --name ${CLUSTER_NAME} \
--state s3://${CLUSTER_NAME} # Append "--yes" to apply changes
kops validate cluster --name ${CLUSTER_NAME} \
--state s3://${CLUSTER_NAME}
A collection of containers
"A pod (as in a pod of whales or pea pod) is a group of
one or more containers (such as Docker containers), with
shared storage/network, and a specification for how to
run the containers."
A collection of pods.
"A Deployment controller provides declarative updates
for Pods and ReplicaSets."
Exposes a pod to the world (or other pods)
FROM fpco/stack-build:lts-9.9 as build
RUN mkdir /opt/build
COPY . /opt/build
VOLUME /tmp/stackroot
RUN cd /opt/build && stack --stack-root=/tmp/stackroot \
build --system-ghc
FROM fpco/pid1
RUN mkdir -p /opt/app
WORKDIR /opt/app
RUN apt-get update && apt-get install -y \
ca-certificates \
libgmp-dev
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
COPY --from=build \
/opt/build/.stack-work/install/x86_64-linux/lts-9.9/8.0.2/bin .
COPY static /opt/app/static
COPY config /opt/app/config
CMD ["/opt/app/myapp"]
More in blog post.
docker build registry.gitlab.fpcomplete.com/fpco-mirrors/haskell-multi-docker-example .
docker push registry.gitlab.fpcomplete.com/fpco-mirrors/haskell-multi-docker-example
docker run -p 3000:3000 -it -w /opt/app \
registry.gitlab.fpcomplete.com/fpco-mirrors/haskell-multi-docker-example \
myapp
Deployment
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: myapp
spec:
template:
metadata:
labels:
app: myapp
spec:
imagePullSecrets:
- name: registry-key
containers:
- name: myapp
image: registry.gitlab.fpcomplete.com/fpco-mirrors/haskell-multi-docker-example:4341
imagePullPolicy: Always
ports:
- name: http
containerPort: 3000
command: ["/opt/app/myapp"]
workingDir: /opt/app
readinessProbe:
httpGet:
path: /
port: 3000
livenessProbe:
httpGet:
path: /
port: 3000
kubectl apply -f deployment.yaml
kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
myapp 1 1 1 1 10m
kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-7b7d556fc9-gzgl7 1/1 Running 0 7m
apiVersion: v1
kind: Service
metadata:
name: myapp
labels:
app: myapp
spec:
ports:
- name: http
port: 80
targetPort: http
selector:
app: myapp
type: LoadBalancer
kubectl apply -f service.yaml
kubctl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
myapp LoadBalancer 100.64.53.144 aacd3aa63aa12... 80:31726/TCP 8m
kubectl describe svc myapp
LoadBalancer Ingress: aacd3aa63aa12...us-east-1.elb.amazonaws.com
kubectl delete svc myapp
kubectl delete deployment myapp
The package manager for kubernetes
helm create chart
Values
replicaCount: 1
image:
repository: registry.gitlab.fpcomplete.com/fpco-mirrors/haskell-multi-docker-example
tag: latest
pullPolicy: IfNotPresent
service:
name: myapp
type: ClusterIP
externalPort: 80
internalPort: 3000
ingress:
enabled: false
...
Deployment
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: {{ template "chart.fullname" . }}
labels:
app: {{ template "chart.name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
template:
metadata:
labels:
app: {{ template "chart.name" . }}
release: {{ .Release.Name }}
spec:
imagePullSecrets:
- name: registry-key
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: {{ .Values.service.internalPort }}
livenessProbe:
httpGet:
path: /
port: {{ .Values.service.internalPort }}
readinessProbe:
httpGet:
path: /
port: {{ .Values.service.internalPort }}
resources:
{{ toYaml .Values.resources | indent 12 }}
{{- if .Values.nodeSelector }}
nodeSelector:
{{ toYaml .Values.nodeSelector | indent 8 }}
{{- end }}
Service
apiVersion: v1
kind: Service
metadata:
name: {{ template "chart.fullname" . }}
labels:
app: {{ template "chart.name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.externalPort }}
targetPort: {{ .Values.service.internalPort }}
protocol: TCP
name: {{ .Values.service.name }}
selector:
app: {{ template "chart.name" . }}
release: {{ .Release.Name }}
## Deploy helm chart
deploy:
@helm upgrade \
--install myapp chart \
-f chart/values/${CI_ENVIRONMENT_NAME}.yaml \
--set image.tag="${CI_PIPELINE_ID}"
Gitlab
build-and-push:
stage: build
script:
- make build-ci-image
- docker login -u gitlab-ci-token -p "${CI_BUILD_TOKEN}" "${CI_REGISTRY}"
- docker push "${CI_REGISTRY_IMAGE}:${CI_PIPELINE_ID}"
deploy_prod:
stage: deploy
script:
- make deploy
environment:
name: production
url: https://k8s-haskell-webinar.fpcomplete.com
when: manual
only:
- master
"Ingress can provide load balancing, SSL termination and name-based virtual hosting."
So...it's Nginx
helm upgrade --install \
fpco-ingress stable/nginx-ingress
{{- if .Values.ingress.enabled -}}
{{- $serviceName := include "chart.fullname" . -}}
{{- $servicePort := .Values.service.externalPort -}}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ template "chart.fullname" . }}
labels:
app: {{ template "chart.name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
annotations:
{{- range $key, $value := .Values.ingress.annotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
rules:
{{- range $host := .Values.ingress.hosts }}
- host: {{ $host }}
http:
paths:
- path: /
backend:
serviceName: {{ $serviceName }}
servicePort: {{ $servicePort }}
{{- end -}}
{{- if .Values.ingress.tls }}
tls:
{{ toYaml .Values.ingress.tls | indent 4 }}
{{- end -}}
{{- end -}}
cat chart/values/production.yaml
ingress:
enabled: true
hosts:
- k8s-haskell-webinar.fpcomplete.com
annotations:
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
tls:
- secretName: k8s-haskell-webinar-tls
hosts:
- k8s-haskell-webinar.fpcomplete.com
Automatic LetsEncrypt certificates
annotations:
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true" <---- HERE
tls:
- secretName: k8s-haskell-webinar-tls
hosts:
- k8s-haskell-webinar.fpcomplete.com
kube-lego
https://github.com/jetstack/kube-lego
(official helm chart stable/kube-lego)
After kube 1.8 use cert-manager
https://github.com/jetstack/cert-manager/
(not production ready)
Automatic route53 entry (other providers are supported)
ingress:
...
hosts:
- k8s-haskell-webinar.fpcomplete.com
(official helm chart stable/external-dns)
Prerequisites:
https://github.com/fpco/terraform-aws-foundation
https://registry.terraform.io/modules/fpco/foundation/aws
module "fpco-dnscontroller" {
source = "fpco/foundation/aws//modules/external-dns-iam"
version = "0.7.5"
name_prefix = "mycluster"
kube_cluster_nodes_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/nodes.${var.k8s_cluster_name}"
}
## prod/values.yaml
enabled:
kube-lego: true
external-dns: true
heapster: false
kubernetes-dashboard: false
kube2iam: true
kube-lego:
config:
# set your email
LEGO_EMAIL: ops@mycompany.com
LEGO_URL: https://acme-v01.api.letsencrypt.org/directory
rbac:
create: true
external-dns:
image:
tag: v0.4.5
podAnnotations:
"iam.amazonaws.com/role": mycluster-dnscontroller
rbac:
create: true
kube2iam:
host:
iptables: true
interface: cni0
rbac:
create: true
extraArgs:
auto-discover-base-arn: true
helm repo add fpco https://s3.amazonaws.com/fpco-charts/stable/
helm update
helm install fpco/foundation --name fpco-foundation --namespace kube-system \
--values=prod/values.yaml
Questions?