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.yamlWhile 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.rolenode.hostnamenode.labels(==,!=,has)
 -  Resources:
deploy.resources.reservationsmap torequestanddeploy.resources.limitsmap tolimit(accept bothcpusandcpukeys)
 -  Toleration: via extra key 
deploy.placement.tolerationswithkubectl taintsyntax -  Service:
portsexposeLoadBlancerby defaultexposeexposesClusterIPservicesnodePortsexposeNodePortservices
 -  Ingress
- Support 
traefik(1.7) labels (deploy.labels) as input with annotations support, including basic auth,PathPrefixStrip,customRequestHeaders,customResponseHeaders... - Support 
CertManager'sIssuerandClusterIssuervia extra labelstraefik.issuerandtraefik.cluster-issuer - Support 
Ingressclass via extra labeltraefik.ingress-class - Support 
segmentlabels 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 
volumeClaimTemplatesforStatefulSet(really useful if combine with cloud provider's dynamic provisioner). - Dynamic provisioner should work as expected. 
volumes.XXX.driver_opts.typemaps directly tostorageClassNameincluding treatments for:none(default storage class)nfsemptyDir
 - Support 
none(map tohostPathifvolumes.XXX.driver_opts.devicepresents) andnfs(ifaddrpresents involumes.XXX.driver_opts.oandvolumes.XXX.driver_opts.deviceprensents) static provisioner. - Support 
readOnlyattribute (volume:/path:rostyle) 
 -  Config: Support top-level configs/external configs
- Support both short and long syntax
 - Data can be integrated directly via 
dataexternal key - Support mouting as directory by setting 
fileto 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 
dataandstringDataexternal keys - Support mouting as directory by setting 
fileto null. See Advance: full override to see how to insert more than one files 
 -  Health check
- Support both 
shellandexecform. For advace features /.e.g/httpGet, please use full override bellow 
 - Support both 
 - Job
 -  CronJob
- A default 
scheduleis 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       2m4sPlease 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,subPathsupport)
 - Volumes
volumes.XXX.storage(string, default1Gifor dynamic provisioner)volumes.XXX.subPath(string, useservices.XXX.volumeslong syntax with extra keysubPathif you want multiplesubPaths
 - Config
config.XXX.file(string | null, required byswarm, can be set tonullto mount config as a directory)config.XXX.data(string)
 - Secret
secrets.XXX.file(string | null, required byswarm, can be set tonullto mount secret as a directory)secrets.XXX.data(string)secrets.XXX.stringData(string)
 - Scheduling:
deploy.placement.tolerations(string[], seekubectl taint -hfor 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 Kinds 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 
traefikheaders - JSON schema of 
docker-composeand 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-runservices.XXX.portswill be exposed asLoadBalancer(if needed)- Additional key 
services.XXX.exposewill be exposed asClusterIPports 
helm -n kube-system upgrade --install traefik link/stack -f test/docker-compose-traefik.yml -f test/docker-compose-traefik-override.yml- Create 
kubernetes-dashboardservice account - Bind it with 
cluster-adminrole 
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+constraintsquotation 
 - v1.9.3: fix tolerations.
 - v1.9.2:
- Fix 
traefik.frontend.rule=PathPrefixStripbehavior for ingress-nginx. - Add 
PathPrefixsupport for ingress-nginx. 
 - Fix 
 - v1.9.1: support 
deploy.placement.tolerationsusingkubectl taintstyle. - 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: truevia docker-compose'network_mode: host. - Add support for docker-compose's long-syntax 
volumesmount. - Add support for volumes/secrets/config 
subPathmount. - 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 
containerskey, 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 
Rawproperty - 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.