Kubernetes External Service with HTTPS

This is a quick example to assign an SSL certificate to a Kubernetes external service(which is an ELB in AWS). Tested with kops 1.8 and kubernetes 1.8.

---
apiVersion: v1
kind: Service
metadata:
 name: my-https-service
 namespace: my-project
 labels:
   app: my-website-ssl
 annotations:
   service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "arn:aws:acm:ap-southeast-2:xxx:certificate/xxx..."
   service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "http"
   service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https"
   service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: '3600'
spec:
 type: LoadBalancer
 selector:
   app: my-website
 ports:
   - name: http
     port: 80
     targetPort: 80
   - name: https
     port: 443
     targetPort: 80

🙂

Get access to a container in Kubernetes cluster

With Kubernetes(K8s), there’s no need to do ssh [email protected] anymore since everything is running as containers. There are still occasions when I need shell access to a container to do some troubleshooting.

With Docker I can do

docker exec -ti <container_id> /bin/bash

It’s quite similar in K8s

kubectl exec -ti <container_id> -- /bin/bash

However in K8s containers have random IDs so I need to know the container ID first

kubectl get pods

Then I can grab the container ID and do the `kubectl exec` command. This is hard to automate because picking up the expected container ID using `grep` and `awk` commands can fail if the matching condition is too strict.

Given a deployment like this

---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 name: my-app-deploy
spec:
 replicas: 2
 template:
   metadata:
     labels:
       app: my-app
...

the K8s way to query the container will be

kubectl get pods --selector=app=my-app -o jsonpath='{.items[0].metadata.name}'

A chained one-liner could be

kubectl exec -ti $(kubectl get pods --selector=app=my-app -o jsonpath='{.items[0].metadata.name}') -- /bin/bash

This doesn’t check for errors, eg. if no container matching `app=my-app` was found, but a better script can be easily crafted from here.

🙂

柳暗花明的2017

对比忙于奔命的2016年, 2017年是收获颇丰但又不失平衡的一年.

在3月份, 我们一家宣誓成为了土澳的公民. 这是我们这几年一直期待的, 当然, 事到临头心情却是复杂的. 另外在公民仪式上我们得到一份礼物, 一盆土澳特有的本地花卉. 我想着本地物种应该很有能耐吧, 就随便把它放在花园里了. 结果冬天没过完它就死了…

有了”身份”后我想着要是有机会去很”土澳”的公司工作一下一定能学到很多”精髓”, 没想到不久之后我很走运的拿到了AFL的工作offer, 没有像以往那样在最后一轮面试时落马. 工作上的收获就不赘述了, 之前的笔记有很多都是这方面的.

二宝在年初时中耳积液, 导致几乎失聪, 之前学会的咿咿呀呀也都忘了. 医生起初认为二宝是自闭儿童, 把我们愁了个不轻. 还是老婆意志力比较强, “毛病再多也得把她养大”, “我们没有别的选择”. 好在给二宝戴助听设备数月之后, 二宝的听力貌似开始恢复了, 逐渐开始响应我们的呼唤. 她并不是自闭, 只是什么都没听到, 生活在静音的世界里.

收获最大的当属大宝笑笑, 终于在老婆的威逼利诱下对家里已经买了二年多的钢琴产生了兴趣. 而且我们在居住区附近找到了大宝的钢琴老师, 让大宝把钢琴+五线谱一起学了. 几个月以后笑笑参加了老师组织的汇报演奏音乐会. 笑笑在绘画方面的进步也很大, 我让她把自己的作品扫描上传到她自己的blog, 但她似乎兴趣不大, 上一次更新停在3月… 笑笑的期末评估也很好, 所有科目都比去年的成绩好(因为去年以及前年我们陪笑笑的时间也少).

今年完成的另一件大事是我帮爸妈提交了移民申请, 希望几年后能顺利团聚. 父母的移民申请基本上是DIY的, 填写的表格可以铺满地板. 感谢老婆的支持以及”过来人”朋友的经验分享. 土澳移民的确越来越难了.

🙂

Internal Service in Kubernetes Cluster

In Kubernetes(K8s) cluster, 1 or more containers form a pod and every container in the pod can access other container’s port just like apps in the same local host. For example:

- pod1
  - nginx1
  - gunicorn1, port:8000

- pod2
  - nginx2
  - gunicorn2, port:8000

So nginx1 can access gunicorn1’s port using localhost:8000 and nginx2 can access gunicorn2, etc… However nginx1 can’t see gunicorn2 using localhost.

When it comes to cache, like redis, I would like a shared redis container(or cluster later) so less memory is wasted and the cache doesn’t need to be warmed for each pod. The structure will look like:

- pod1
  - nginx1
  - gunicorn1
- pod2
  - nginx2
  - gunicorn2
- pod3
  - redis

To grant both gunicorns to access redis, redis needs to be a service:

---
apiVersion: v1
kind: Service
metadata:
  name: redis-svc
  labels:
    app: redis
    role: cache
spec:
  type: NodePort
  ports:
    - port: 6379
  selector:
    app: redis
    role: cache

And the deployment to support the service looks like:

---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: redis-deploy
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: redis
        role: cache
    spec:
      containers:
        - name: redis
          image: redis:4
          resources:
            requests:
              memory: 200Mi
          ports:
            - containerPort: 6379

Then in the settings.py of the django app running in gunicorn container, redis can be accessed via ENVs setup by K8s:

CACHES = {
  "default": {
    "BACKEND": "django_redis.cache.RedisCache",
    "LOCATION": "redis://{0}:{1}/1".format(os.environ['REDIS_SVC_SERVICE_HOST'], os.environ['REDIS_SVC_SERVICE_PORT']),
    "OPTIONS": {
      "CLIENT_CLASS": "django_redis.client.DefaultClient"
    },
  }
}

🙂