Deploy your docker-compose
stack
with Helm.
If you ever ask yourself, what do this thousand lines of k8s
manifest or that monstrous helm chart does behind the scene, this chart may be what you were waiting for so long.
helm repo add link https://linktohack.github.io/helm-stack/
kubectl create namespace your-stack
# docker stack deploy -c docker-compose.yaml your_stack
helm -n your-stack upgrade --install your-stack link/stack -f docker-compose.yaml
While the inter-container communication is enabled in swarm
either by network
or link
, in k8s
if you have more than one service and they need to communicate together, you will need to expose the ports explicitly by --set services.XXX.expose={YYYY}
The chart is features complete and I was able to deploy complex stacks with it including traefik
and kubernetes-dashboard
. In all cases, there is a mechanism to override the generated manifests with full possibilities of k8s
API (see below.)
Acceptable configurations can be found in the test:.
- Deployment:
- Affinity: Support placement constraints (
deploy.placement.constraints
) including:node.role
node.hostname
node.labels
(==
,!=
,has
)
- Resources:
deploy.resources.reservations
map torequest
anddeploy.resources.limits
map tolimit
(accept bothcpus
andcpu
keys)
- Toleration: via extra key
deploy.placement.tolerations
withkubectl taint
syntax - Service:
ports
exposeLoadBlancer
by defaultexpose
exposesClusterIP
servicesnodePorts
exposeNodePort
services
- Ingress
- Support
traefik
(1.7) labels (deploy.labels
) as input with annotations support, including basic auth,PathPrefixStrip
,customRequestHeaders
,customResponseHeaders
... - Support
CertManager
'sIssuer
andClusterIssuer
via extra labelstraefik.issuer
andtraefik.cluster-issuer
- Support
Ingress
class via extra labeltraefik.ingress-class
- Support
segment
labels for services that expose multiple portstraefik.port
,traefik.first.port
,traefik.second.port
...
- Support
- Volume: Support inline/top-level volumes/external volumes
- Support both short and long syntax
- Automatic switch to
volumeClaimTemplates
forStatefulSet
(really useful if combine with cloud provider's dynamic provisioner). - Dynamic provisioner should work as expected.
volumes.XXX.driver_opts.type
maps directly tostorageClassName
including treatments for:none
(default storage class)nfs
emptyDir
- Support
none
(map tohostPath
ifvolumes.XXX.driver_opts.device
presents) andnfs
(ifaddr
presents involumes.XXX.driver_opts.o
andvolumes.XXX.driver_opts.device
prensents) static provisioner. - Support
readOnly
attribute (volume:/path:ro
style)
- Config: Support top-level configs/external configs
- Support both short and long syntax
- Data can be integrated directly via
data
external key - Support mouting as directory by setting
file
to null. See Advance: full override to see how to insert more than one files
- Secret: Support top-level secrets/external secrets
- Support both short and long syntax
- Data can be integrated directly via
data
andstringData
external keys - Support mouting as directory by setting
file
to null. See Advance: full override to see how to insert more than one files
- Health check
- Support both
shell
andexec
form. For advace features /.e.g/httpGet
, please use full override bellow
- Support both
- Job
- CronJob
- A default
schedule
is set as*/1 * * * *
but it can be easily overwritten withCronJob.spec.schedule
.
- A default
Tested in a K3s cluster with local-path
provisioner.
❯ helm -n com-linktohack-docker-on-compose upgrade --install sample link/stack -f test/docker-compose-dockersamples.yaml
Release "sample" does not exist. Installing it now.
NAME: sample
LAST DEPLOYED: Tue Jan 14 18:38:42 2020
NAMESPACE: com-linktohack-docker-on-compose
STATUS: deployed
REVISION: 1
TEST SUITE: None
❯ kubectl get all -n com-linktohack-docker-on-compose stack/git/master
NAME READY STATUS RESTARTS AGE
pod/svclb-web-loadbalancer-tcp-hk9sb 1/1 Running 0 2m2s
pod/web-57bbd888fb-dvqxj 1/1 Running 0 2m2s
pod/db-769769498d-6zqx8 1/1 Running 0 2m2s
pod/words-6465f956d-kmk9c 1/1 Running 0 2m2s
pod/words-6465f956d-sw9t2 1/1 Running 0 2m2s
pod/words-6465f956d-vchlm 1/1 Running 0 2m2s
pod/words-6465f956d-l9lnd 1/1 Running 0 2m2s
pod/words-6465f956d-2lsbz 1/1 Running 0 2m2s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/web-loadbalancer-tcp LoadBalancer 10.43.235.241 2.56.99.175 33000:31908/TCP 2m4s
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/svclb-web-loadbalancer-tcp 1 1 1 1 1 <none> 2m4s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/web 1/1 1 1 2m4s
deployment.apps/db 1/1 1 1 2m4s
deployment.apps/words 5/5 5 5 2m4s
NAME DESIRED CURRENT READY AGE
replicaset.apps/web-57bbd888fb 1 1 1 2m4s
replicaset.apps/db-769769498d 1 1 1 2m4s
replicaset.apps/words-6465f956d 5 5 5 2m4s
Please see below.
These keys are either not existed in docker-compose
format or have the meaning changed. They're should be set via --set
or second values.yaml
.
- Services
services.XXX.kind
(string, overrides automatic kind detection:Deployment
,DaemonSet
,StatefulSet
)services.XXX.imagePullSecrets
(string, name of the secret)services.XXX.imagePullPolicy
(string)services.XXX.serviceAccountName
(string)services.XXX.expose
(array, ports to be exposed for other services viaClusterIP
)services.XXX.ports
(array, ports to be exposed viaLoadBalancer
)services.XXX.nodePorts
(ports to be exposed asNodePort
)services.XXX.containers
(array, same spec asservices.XXX
, additional containers to run in the samePod
)services.XXX.initContainers
(array, same spec asservices.XXX.containers
, populatespod.spec.initContainers
)services.XXX.volumes[].subPath
(string,subPath
support)
- Volumes
volumes.XXX.storage
(string, default1Gi
for dynamic provisioner)volumes.XXX.subPath
(string, useservices.XXX.volumes
long syntax with extra keysubPath
if you want multiplesubPath
s
- Config
config.XXX.file
(string | null, required byswarm
, can be set tonull
to mount config as a directory)config.XXX.data
(string)
- Secret
secrets.XXX.file
(string | null, required byswarm
, can be set tonull
to mount secret as a directory)secrets.XXX.data
(string)secrets.XXX.stringData
(string)
- Scheduling:
deploy.placement.tolerations
(string[], seekubectl taint -h
for syntax)
- Top levels
chdir
(string, required in case of rusing relative paths in volumes)Raw
(array, manifests that should be deployed as is)
Raw
property allows us to deploy arbitrary manifests, but most of time, there is a better way.
The properties of the manifests can be overridden (merged) with the values from services.XXX.Kind
and volumes.XXX.Kind
...
You will now have full control of the output manifests. While this is a deep merge operation, the item in the list will be also merged if existed, new items will be also inserted.
The full list of all the Kind
s can be found in the listing below, please note that services.XXX.imagePullPolicy
, volumes.XXX.storage
, configs.XXX.data
secrets.XXX.stringData
are already recognized as extra keys.
services:
redmine:
ClusterIP: {}
NodePort: {}
LoadBalancer:
tcp: {}
upd: {}
Ingress:
default: {} # default segement
seg1: {} # segment seg1
Auth:
default: {}
seg: {} # segment seg1
Deployment:
spec:
template:
spec:
containers:
- name: override-name
imagePullPolicy: Always # supported as an extra key already
DaemonSet:
spec:
StatefulSe:
spec:
Job:
spec:
CronJob:
spec:
schedule: '*/1 * * * *' # mostly require
volumes:
db:
PV:
spec:
capacity:
storage: 10Gi # supported as an extra key already
persistentVolumeReclaimPolicy: Retain
PVC:
spec:
resources:
requests:
storage: 10Gi # supported as an extra key already
configs:
redmine_config:
ConfigMap:
data:
hello.yaml: there
secrets:
with_string_data:
Secret:
stringData: ""
Golang template + Sprig are quite a pleasure to work as a full-feature language.
Blog post https://linktohack.com/posts/evaluate-options-to-migrate-from-swarm-to-k8s/
The same technique can be applied via a proper language instead of using a Helm template but why not standing on the shoulders of giant(s). By using Helm (the de facto package manager) we're having the ability rollback and so on... for free.
- More
traefik
headers - JSON schema of
docker-compose
and extra keys - More tests
This example contains almost all the possible configurations of this stack.
helm -n com-linktohack-redmine upgrade --install redmine link/stack -f test/docker-compose-redmine.yaml -f test/docker-compose-redmine-override.yaml \
--set services.db.expose={3306:3306} \
--set services.db.ports={3306:3306} \
--set services.db.deploy.placement.constraints={node.role==manager} \
--set services.redmine.deploy.placement.constraints={node.role==manager} \
--set chdir=/stack --debug --dry-run
services.XXX.ports
will be exposed asLoadBalancer
(if needed)- Additional key
services.XXX.expose
will be exposed asClusterIP
ports
helm -n kube-system upgrade --install traefik link/stack -f test/docker-compose-traefik.yml -f test/docker-compose-traefik-override.yml
- Create
kubernetes-dashboard
service account - Bind it with
cluster-admin
role
helm -n kubernetes-dashboard upgrade --install dashboard link/stack -f test/docker-compose-kubernetes-dashboard.yml
helm -n com-linktohack-redmine template redmine link/stack -f test/docker-compose-redmine.yaml -f test/docker-compose-redmine-override.yaml \
--set services.db.expose={3306:3306} \
--set services.db.ports={3306:3306} \
--set services.db.deploy.placement.constraints={node.role==manager} \
--set services.redmine.deploy.placement.constraints={node.role==manager} \
--set chdir=/stack --debug > test/docker-compose-redmine.manifest.yaml
kubectl -n com-linktohack-redmine apply -f test/docker-compose-redmine.manifest.yaml
- v1.25.0: CronJob: Updade apiVersion to
v1
(require k8s v1.25) - v1.22.0: Ingress: Update apiVersion to
v1
(require k8s v1.22) - v1.18.1: Update Ingress's apiVersion to
networking.k8s.io/v1beta1
- v1.18.0: Support k8s version between 1.18 and 1.21
- Support
ingressClassName
- Support
- v1.16.0: Starting from this version, we follow k8s's versioning scheme so that 1.16.x series supports k8s version is between 1.16 and 1.21
- Add tests, require https://pypi.org/project/yq/. More test are welcome
- Add more nginx annotations
- Fix missing
chidir
+constraints
quotation
- v1.9.3: fix tolerations.
- v1.9.2:
- Fix
traefik.frontend.rule=PathPrefixStrip
behavior for ingress-nginx. - Add
PathPrefix
support for ingress-nginx.
- Fix
- v1.9.1: support
deploy.placement.tolerations
usingkubectl taint
style. - v1.9.0:
- Support docker-compose style resources requests/limits via
services.XXX.deploy.resources
. - Add support for extra key
initContainers
. - Support Kubernetes Pod
hostNetwork: true
via docker-compose'network_mode: host
. - Add support for docker-compose's long-syntax
volumes
mount. - Add support for volumes/secrets/config
subPath
mount. - Fix: StatefulSet should now honor volumes with
external: true
(not create avolumeClaimTemplate
).
- Support docker-compose style resources requests/limits via
- v1.8.6 Support extra
containers
key, withmergeDeepOvewrite
- v1.7.0 Support
Job
&CronJob
- v1.6.0 Allow to mount static path to
StatefulSet
. - v1.5.0 Support
CertManager
- v1.4.0 with
Raw
property - v1.3.7 Support port range
xxxx-yyyy:zzzz-tttt/udp
Copyright (c) 2023 Quang-Linh LE
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
For Enterprise Licensing:
If you want to use this software in an enterprise environment, please contact me directly for further information and licensing arrangements.