Live data from Hacker News

Connecting Kubernetes services with linkerd

lwn.net

41–49 of 49 posts

Re: Connecting Kubernetes services with linkerd

#41

Earlier quoted context omitted.

The whole point of Linkerd is moving logic out of your app. Linkerd is like a library but, instead of being in-process, it inserts itself between the app and whatever the app needs to talk to. The app just dials http://foo.svc/ , and Linkerd sends the connection to the right place; it doesn't need to know where foo.svc is. Many companies, including mine, already run an internal load-balancer (such as HAProxy) that ta…

> The whole point of Linkerd is moving logic out of your app. Linkerd is like a library but, instead of being in-process, it inserts itself between the app and whatever the app needs to talk to. The app just dials http://foo.svc/ , and Linkerd sends the connection to the right place; it doesn't need to know where foo.svc is. That's just DNS. > Many companies, including mine, already run an internal load-balancer (suc…

> That's just DNS.

That's not just DNS. Well, it might be - it depends on your environment.

For example: In a world where you have some kind of containerization, or scheduler responsible for deploying your services you typically to have services (or different instances of your services), running on ports determined at runtime. You might solve this with an off-host load balancer, so your frontend applications don't need to worry about it, but now you need to worry about those other things: circuit breaking, retry mechanisms, etc between your application and its middle-tear load balancer.

A common deployment strategy for something like linkerd (or Envoy) would be as a sidecar process, on the same host where you don't need to worry about the reliability of the network to talk to a remote service, because something else is taking care of 90% of your concerns for you.

> Why not DNS round-robin?

There's functionality offered by load balancing at L7 that you can't do with round-robin DNS. You end up having to push that complexity into each service you develop - retry mechanisms, circuit breaking, and routing decisions if you're going to try do green/blue or canary deployments.

You might say "solve this with a library", but the reality is you have to reinvent the wheel, you have to keep that library in sync to ensure the behaviour is consistent, and if you're in an organization that develops services in "N" languages, that challenge can be even greater.

> Fundamentally, the application itself needs to be resilient. No proxy will make it so. It could help, but it can't do it.

Absolutely. Your services still need to be able to fail gracefully, but this just moves the complexity of a whole host of other issues to a single, and common spot.

Re: Connecting Kubernetes services with linkerd

#42
post #29

Earlier quoted context omitted.

I don't think SRV records are the right answer; your networking layer should be k8s aware and issue a watch command on Endpoints; it'll be updated immediately-ish when the servers changes. This is similar to how finagle's zookeeper server set is supposed to work. What linkerd buys you is you don't have to write this type k8s-aware, zipkin logging library for every language you're running in production. But I think it…

> I don't think SRV records are the right answer; OK, A/AAAA are more common. > your networking layer should be k8s aware and issue a watch command on Endpoints; Or, I could use standard networking concepts and not build my entire app to suit one new thing that I may not want to stay with forever, or even more than just test. > What linkerd buys you is you don't have to write this type k8s-aware, zipkin logging libra…

DNS for service discovery is fraught with perils. Many implementations don't respect TTLs, or don't actually use multiple records, or don't do something smart like power of two.

Even if you pick all your impls carefully, you have to wait for your TTL instead of a push mechanism for changes.

If you wanted to implement the watch as a library, code wise, it's maybe ~500 lines per language, using the k8s client lib. I could hammer it out in two days.

Re: Connecting Kubernetes services with linkerd

#43

Earlier quoted context omitted.

The whole point of Linkerd is moving logic out of your app. Linkerd is like a library but, instead of being in-process, it inserts itself between the app and whatever the app needs to talk to. The app just dials http://foo.svc/ , and Linkerd sends the connection to the right place; it doesn't need to know where foo.svc is. Many companies, including mine, already run an internal load-balancer (such as HAProxy) that ta…

> The whole point of Linkerd is moving logic out of your app. Linkerd is like a library but, instead of being in-process, it inserts itself between the app and whatever the app needs to talk to. The app just dials http://foo.svc/ , and Linkerd sends the connection to the right place; it doesn't need to know where foo.svc is. That's just DNS. > Many companies, including mine, already run an internal load-balancer (suc…

> That's just DNS.

No, it's a bit more complicated than that, as chrisboulton explains (so I won't go into detail).

You could use a dynamic DNS server such as Consul for discovery, and people do. Last I checked, you still had to fight eagerly caching clients, but that's probably doable. But there's still a host of other issues that Linkerd addresses.

Again, I strongly recommend reading about Finagle to understand what this is trying to solve.

Another thing Linkerd brings to the mix is instrumentation and tracing. Instead of building this into every single sender and receiver, Linkerd can collect tracing data for you and make it available for other subsystems to ingest, such as Zipkin or Prometheus.

Re: Connecting Kubernetes services with linkerd

#44

Earlier quoted context omitted.

I recognize that Docker and k8s are distinct, and I think rkt looks much smarter than Docker. Now that k8s has released a stable version capable of running non-Docker containers (as of last week), it's fair to separate them. However, I'm still struggling to see the real value proposition offered by either. The story on Docker or Docker-style containers without orchestration is that they're supposed to be easy to ensu…

Being a Puppet user (until we've finally retired that nightmare), I couldn't be happier with this migration to containers. Docker itself is problematic for all sorts of reasons, but it at least does the most important thing, which is to run pre-packaged code in isolated containers. With Puppet, as with Ansible, you just can't guarantee an isolated environment, nor is the environment easily "replicable", in the sense…

>The only unit of provisioning is the VM. Puppet/Salt/Ansible are highly static systems.

If you're operating the same script from the same image (say, a distro's official ISO), they should work effectively the same way. It is true that bootstrapping nodes may break, but unlikely. It shouldn't be common.

There's no reason something like Ansible couldn't be extended to build containers. Playbooks effectively perform the same function as Dockerfiles: they list commands to execute off of a base image. Playbooks are somewhat more flexible in that they're frequently used to address already-running machines.

For the record, push-centric configuration management like Chef and Puppet is much worse than Salt or Ansible, which can connect to arbitrary nodes and execute commands.

>Kubernetes smoothes over a lot of the rough parts of Docker: Ports, IPs, DNS/discovery, monitoring and volume management are all neatly captured by Kubernetes

Maybe we adopted too early but none of these things are "neatly" captured by Kubernetes. We have complex superstructures over all of them. They may be neater than running them in Docker alone, but they're much more complex than running against bare nodes.

Particularly, addressing application instances and getting stateful storage have been challenges in Kubernetes, just as they are in Docker. StatefulSets went stable in December (3 months ago) and I haven't tried them yet, so maybe those concerns are assuaged.

The ideal interface for k8s would be, essentially, an aware hypervisor. If each pod acted as a subnet and each container acted like a machine in that subnet, meaning it had an IP that was accessible according to external firewall and DNS rules and otherwise acted like a normal machine, that'd be much easier to accept. Neither Docker nor Kubernetes are amenable to this.

>It also handles exec'ing into an existing container.

The interface for this is practically the same as it is on Docker. You do `kubectl exec -it /bin/ash` instead of `docker exec -it /bin/ash`. The only extra thing it handles is making the container appear local to the master, instead of requiring one to get on the correct kubelet.

>For me, the value proposition is the cloud. When I boot up N nodes, I know that Kubernetes will optimally pack them with my container, and if I boot up N+1 nodes, I know that the containers will spread to the new node, and back down again if I go down to N (either intentionally or accidentally).

The value in this being running more applications on a single cloud instance? If you have the instance/VM as the unit of orchestration, it works the same way: a coordinator (load balancer) divides the traffic among the units, and then stops transparently when you deregister or when a health check fails.

>Orchestration, scheduling, autoscaling etc. take care of this "cloud of containers", if you will, and since every container is an isolated, identical, versioned, stateless (for the most part!), fully redundant thing

"Orchestration, scheduling, autoscaling etc." refer, more or less, to the same thing: getting your applications running somewhere. Containers and k8s in no way have a monopoly on that functionality, and my experience has not been that they've significantly simplified it.

>I rest assured that what is running is what I deployed.

If running a playbook over a fresh node isn't good enough, you can use an image system to ensure it's a binary copy of what you built. There were several competing APIs for managing this in a platform-agnostic manner.

>Then there are all the other nice things, like rolling deploys, cordoning, self-healing, job scheduling, secret management, being able to boot up a parallel, different version of a stack without clobbering everything else, etc.

I know that k8s includes some logic to attempt to automate the failover/deployment process, and that is a nice bonus. Not without equivalents, but I believe that k8s is positioned to do this more reliably than other systems.

Otherwise, same story here; containers and k8s did not introduce these capabilities, and I'm not sure how it claims to improve them.

k8s seems necessary because it makes all of this that already had reasonable solutions on non-containers work well with containers. Thus, no containers, no real reason to use k8s.

>We run production and staging in the same Kubernetes cluster (different namespaces), and we can use things like resource requests/limits and node affinities to ensure that they're scheduled differently, but still using the same underlying hardware resources.

Setting request quotas and node affinities is not fun. In a busy environment, it's like managing a shared host; you have to fine-tune each application so that it doesn't go outside of its box, and this is not completely reliable if anything else is consuming resources (say, dockerd, which was recently running at 450% utilization on one of our instances).

>What pains are you referring to, specifically?

a) Redoing all applications so they can run without a permanent filesystem, so they can be prepared for pod termination at any moment, so they don't care about being individually addressable, and so on. This by itself is a big deal. Containers etc don't make sense for a stateful project, and there's no reason to assume that every project needs to be stateless. "Operators" have been suggested as CoreOS as a way to handle this, but that's a whole 'nother headache to add to the pile, requiring registration of third-party resource types and listeners to gracefully handle pod termination.

b) Creating usable Dockerfiles for everything and maintaining them. In our case, we converted to Alpine because it "made smaller images", which was also a pain point due to lots of things not being friendly with musl. This includes tons of troubleshooting, weird little tricks, injecting things into the right place in the Dockerfile to keep the image size minimized, trying to map ports and do all the esoteric invocations required by the Docker daemon when running and linking the containers, clearing out images and containers before they make your disk full since Docker doesn't know how to do this intelligently, and so forth.

c) Getting everything to talk to the things it needs through Kubernetes, which involves a lot of config and port mapping, whereas previously we only needed to say "Go to DNS name" within our LAN (firewall managed separately). Kubernetes sort of allows this, for a subset of containers, if they're in the right cluster and the right label has been applied or something. Ingress resources and controllers are filed under this umbrella.

d) Troubleshooting or analyzing the live environment is much more difficult due to all of the weird network shenanigans going on, the restricted context available within the container, lack of diagnostic tools, etc.

e) Kubernetes configs are long, esoteric, and rely on a complex web of interdependent k8s-specific objects. Deployment success/failure takes multiple steps to ascertain (deploy; get pods; describe pod if failure; look for relevant message and respond, which requires repeating the process).

f) Docker hanging and breaking all containers until host reboot; Docker eating all disk space during development (for images/builds, usually); Docker crashing randomly; etc.

Say you want to interactively test a change to something that's running in a Docker container. You can get into the container and edit the file in place (if you install vim or similar), but beware that restarting the process will delete your container and revert back to the image. Your options are a) edit the file in your local environment, rebuild the Dockerfile locally, re-run the image, re-trigger the event, observe bugs, and repeat; or b) circumvent by doing docker start ; docker exec -it /your/shell; and running the processes, detaching from them, and editing the process in-container, retriggering the processes that would be fired by the CMD or ENTRYPOINT each time to reload the application; hope all of this works fine in the limited context provided by the container. Then, exiting the container, committing the image, pulling out your changes to your local environment where you can commit them, pushing to git and the image registry, and hoping that the Docker container, which hypothetically is supposed to work automatically everywhere, actually ends up doing that, which seems not to happen pretty often for something that's supposed to work everywhere.

OR you could spin up a test instance/VM/whatever, or set up a test version of the application on your local environment, BOTH of which can usually be done with the same Ansible playbook, and then you can edit normally without worrying about the filesystem disappearing or the repeated builds taking all your space and test normally without worrying about esoteric error messages due to the limited container environment, restricted sandbox permissions, or incorrect network flags on the container invocation. You run tests, commit the change, which is not at risk of disappearing, and move on.

Anyway, if you're already committed to Docker for whatever reason, Kubernetes is probably an OK way to generalize it and try to make it less bad. I just see no compelling reason to commit to either other than a significant concern of maximum utilization of cloud instances, which is a fine concern, but not in exchange for the extra cost incurred in k8s.

Maybe my opinion would change if I stepped away from our environment and tried to bootstrap my own "clean" k8s cluster and Dockerfiles, but my experiments with minikube have featured a lot of the same frustrations.

Everything just seems to take a lot longer this way, which makes sense because we've added many layers of complication on top of what was a previously well-defined workflow. Containers could be nice if they were more mature, but for now, there is every reason to stay away.

Re: Connecting Kubernetes services with linkerd

#45

Lyft has a tech similar to linkerd called Envoy: https://github.com/lyft/envoy . It is a bit lower level than linkerd (based on APIs rather than already integrated with common services) and written in C++. I'm working on an adapter which should offer a better integration with Kubernetes. The end goal is to have envoy be able to replace our Ingress controllers. Not having to run the JVM on every machine is also nice.

Folks at Lyft/IBM/Google are building a service mesh partly based on Envoy over here: https://istio.io

Re: Connecting Kubernetes services with linkerd

#46

Earlier quoted context omitted.

The whole point of Linkerd is moving logic out of your app. Linkerd is like a library but, instead of being in-process, it inserts itself between the app and whatever the app needs to talk to. The app just dials http://foo.svc/ , and Linkerd sends the connection to the right place; it doesn't need to know where foo.svc is. Many companies, including mine, already run an internal load-balancer (such as HAProxy) that ta…

> The whole point of Linkerd is moving logic out of your app. Linkerd is like a library but, instead of being in-process, it inserts itself between the app and whatever the app needs to talk to. The app just dials http://foo.svc/ , and Linkerd sends the connection to the right place; it doesn't need to know where foo.svc is. That's just DNS. > Many companies, including mine, already run an internal load-balancer (suc…

> Why not DNS round-robin?

Round-robin may be sufficient for your needs. However, the load balancing techniques that linkerd employs solved real production issues for us. These techniques allowed us to be paged less frequently for minor errors and we could more reliably make changes to the system without causing user-facing issues.

See https://blog.buoyant.io/2016/03/16/beyond-round-robin-load-b...

Re: Connecting Kubernetes services with linkerd

#47
post #32

Earlier quoted context omitted.

>I think the habit for most folks (and the best practice) is to use one of the "official" Ruby images. Ah, I didn't realize there was a blessed subset of repositories for which the Docker team has independently verified the identity. Are these signed with some sort of crypto and known-good keys, similar to package signing keys attached to a package repo, or are we taking Docker's word for it? Is there a way to filter…

I believe most of your questions have a (mostly) positive answer. Admittedly the Docker project moves fast, so I've tried to provide a link or two for each: > Are these signed with some sort of crypto and known-good keys, similar to package signing keys attached to a package repo, or are we taking Docker's word for it? The official images are all signed with Notary[0] (released > I had to go 3 levels up from the link…

Thanks for this post. It does indeed answer most of my follow-up questions.

I wanted to let you know that I gave `docker build --squash` a try today. This is the output I got:

    "--squash" is only supported on a Docker daemon with experimental features enabled
so that feature is not yet mainlined.

Also, I encountered another issue today when trying to do a `docker push`. It retried a cache layer multiple times before conceding with `open /dev/mapper/docker-254:0-xxx-yyy-zzz: no such file or directory` on one of the layers. I had to rebuild the image with `--no-cache` to get it to push.

Not doing anything unusual/fancy, just an ordinary docker push, which worked fine on other images before and after. No auto-cleanup scripts running in the background or anything that should cause a layer to go mysteriously missing.

Re: Connecting Kubernetes services with linkerd

#48

Earlier quoted context omitted.

> The whole point of Linkerd is moving logic out of your app. Linkerd is like a library but, instead of being in-process, it inserts itself between the app and whatever the app needs to talk to. The app just dials http://foo.svc/ , and Linkerd sends the connection to the right place; it doesn't need to know where foo.svc is. That's just DNS. > Many companies, including mine, already run an internal load-balancer (suc…

> That's just DNS. That's not just DNS. Well, it might be - it depends on your environment. For example: In a world where you have some kind of containerization, or scheduler responsible for deploying your services you typically to have services (or different instances of your services), running on ports determined at runtime. You might solve this with an off-host load balancer, so your frontend applications don't ne…

>That's not just DNS. Well, it might be - it depends on your environment.

In short, another problem introduced by containers and not relevant to non-containers. Why can't a container get its own IP and port like everything else? FreeBSD jails do. Why make it opaque and put the "ingress" component inside of k8s or Docker? It'd be reasonable for k8s to act as a DHCP/DNS server, but not to swallow the whole world.

Re: Connecting Kubernetes services with linkerd

#49

Earlier quoted context omitted.

I recognize that Docker and k8s are distinct, and I think rkt looks much smarter than Docker. Now that k8s has released a stable version capable of running non-Docker containers (as of last week), it's fair to separate them. However, I'm still struggling to see the real value proposition offered by either. The story on Docker or Docker-style containers without orchestration is that they're supposed to be easy to ensu…

Being a Puppet user (until we've finally retired that nightmare), I couldn't be happier with this migration to containers. Docker itself is problematic for all sorts of reasons, but it at least does the most important thing, which is to run pre-packaged code in isolated containers. With Puppet, as with Ansible, you just can't guarantee an isolated environment, nor is the environment easily "replicable", in the sense…

I know I was just downvoted for answering your questions before, but I thought I'd reply here just to share what I've hit so far during today's work day using Docker. Just today.

1. `docker push` fails because of a mysteriously-vanished cache layer, forcing `docker build --no-cache` to work.

2. `docker-compose stop [service]` broke in a weird way, failing to detect the service's stoppage, creating a duplicate service of the same name that I couldn't do anything with. Had to clear out the duplicate container with `docker rm -fv ...` and now trying to figure out how to make `docker-compose` happy again.

These are totally normal, conventional operations that should not be choking for unexplained reasons.

And people suggest running production workloads on this, for some reason.

and bonus:

3. Tried the `docker build --squash` feature suggested by another commenter, to find it requires "experimental support" to be enabled in the Docker daemon. I have enough problems with docker without that, so I'm going to pass.

Post reply on HN