Skip to content

Commit

Permalink
feat: support reference model for workloads (argoproj#676)
Browse files Browse the repository at this point in the history
Signed-off-by: Alexander Matyushentsev <[email protected]>
  • Loading branch information
alexmt committed Apr 9, 2021
1 parent 959a4a5 commit a634ec4
Show file tree
Hide file tree
Showing 17 changed files with 935 additions and 142 deletions.
4 changes: 4 additions & 0 deletions cmd/rollouts-controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/dynamic/dynamicinformer"
kubeinformers "k8s.io/client-go/informers"
Expand Down Expand Up @@ -97,6 +98,8 @@ func newCommand() *cobra.Command {
checkError(err)
dynamicClient, err := dynamic.NewForConfig(config)
checkError(err)
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
checkError(err)
smiClient, err := smiclientset.NewForConfig(config)
resyncDuration := time.Duration(rolloutResyncPeriod) * time.Second
kubeInformerFactory := kubeinformers.NewSharedInformerFactoryWithOptions(
Expand Down Expand Up @@ -130,6 +133,7 @@ func newCommand() *cobra.Command {
rolloutClient,
dynamicClient,
smiClient,
discoveryClient,
kubeInformerFactory.Apps().V1().ReplicaSets(),
kubeInformerFactory.Core().V1().Services(),
kubeInformerFactory.Extensions().V1beta1().Ingresses(),
Expand Down
9 changes: 9 additions & 0 deletions controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"k8s.io/apimachinery/pkg/util/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
appsinformers "k8s.io/client-go/informers/apps/v1"
batchinformers "k8s.io/client-go/informers/batch/v1"
Expand Down Expand Up @@ -85,6 +86,8 @@ type Manager struct {
experimentWorkqueue workqueue.RateLimitingInterface
analysisRunWorkqueue workqueue.RateLimitingInterface

refResolver rollout.TemplateRefResolver

dynamicClientSet dynamic.Interface

namespace string
Expand All @@ -97,6 +100,7 @@ func NewManager(
argoprojclientset clientset.Interface,
dynamicclientset dynamic.Interface,
smiclientset smiclientset.Interface,
discoveryClient discovery.DiscoveryInterface,
replicaSetInformer appsinformers.ReplicaSetInformer,
servicesInformer coreinformers.ServiceInformer,
ingressesInformer extensionsinformers.IngressInformer,
Expand Down Expand Up @@ -141,11 +145,14 @@ func NewManager(
serviceWorkqueue := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "Services")
ingressWorkqueue := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "Ingresses")

refResolver := rollout.NewInformerBasedWorkloadRefResolver(namespace, dynamicclientset, discoveryClient, rolloutWorkqueue, rolloutsInformer.Informer())

rolloutController := rollout.NewController(rollout.ControllerConfig{
Namespace: namespace,
KubeClientSet: kubeclientset,
ArgoProjClientset: argoprojclientset,
DynamicClientSet: dynamicclientset,
RefResolver: refResolver,
SmiClientSet: smiclientset,
ExperimentInformer: experimentsInformer,
AnalysisRunInformer: analysisRunInformer,
Expand Down Expand Up @@ -238,6 +245,7 @@ func NewManager(
experimentController: experimentController,
analysisController: analysisController,
dynamicClientSet: dynamicclientset,
refResolver: refResolver,
namespace: namespace,
}

Expand All @@ -255,6 +263,7 @@ func (c *Manager) Run(rolloutThreadiness, serviceThreadiness, ingressThreadiness
defer c.rolloutWorkqueue.ShutDown()
defer c.experimentWorkqueue.ShutDown()
defer c.analysisRunWorkqueue.ShutDown()

// Wait for the caches to be synced before starting workers
log.Info("Waiting for controller's informer caches to sync")
if ok := cache.WaitForCacheSync(stopCh, c.serviceSynced, c.ingressSynced, c.jobSynced, c.rolloutSynced, c.experimentSynced, c.analysisRunSynced, c.analysisTemplateSynced, c.replicasSetSynced); !ok {
Expand Down
86 changes: 81 additions & 5 deletions docs/migrating.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Migrating to Rollouts

Migrating to Argo Rollouts involves converting an existing Deployment resource, to a Rollout resource.
There are ways to migrate to Rollout:

* Convert an existing Deployment resource to a Rollout resource.
* Reference an existing Deployment from a Rollout using `workloadRef` field.

## Convert Deployment to Rollout

When converting a Deployment to a Rollout, it involves changing three fields:

Expand Down Expand Up @@ -36,8 +41,79 @@ spec:
- pause: {}
```
## Other Considerations
!!! warning
When migrating a Deployment which is already serving live production traffic, a Rollout should
run next to the Deployment before deleting the Deployment or scaling down the Deployment.
**Not following this approach might result in downtime**. It also allows for the Rollout to be
tested before deleting the original Deployment.
## Reference Deployment From Rollout
Instead of removing Deployment you can scale-down in to zero and reference from the Rollout resource:
1. Create a Rollout resource.
1. Reference an existing Deployment using `workloadRef` field.
1. Scale-down existing Deployment by changing `replicas` field of an existing Deployment to zero.

Below is an example of a Rollout resource referencing a Deployment.

```yaml
apiVersion: argoproj.io/v1alpha1 # Create a rollout resource
kind: Rollout
metadata:
name: rollout-ref-deployment
spec:
replicas: 5
workloadRef: # Reference an existing Deployment using workloadRef field
apiVersion: apps/v1
kind: Deployment
name: rollout-ref-deployment
strategy:
canary:
steps:
- setWeight: 20
- pause: {duration: 10s}
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/instance: rollout-canary
name: rollout-ref-deployment
spec:
replicas: 0 # Scale down existing deployment
selector:
matchLabels:
app: rollout-ref-deployment
template:
metadata:
labels:
app: rollout-ref-deployment
spec:
containers:
- name: rollouts-demo
image: argoproj/rollouts-demo:blue
imagePullPolicy: Always
ports:
- containerPort: 8080
```

Consider following if your Deployment runs in production:

**Running Rollout and Deployment side-by-side**

After creation Rollout will spinup required number of Pods side-by-side with the Deployment Pods.
Rollout won't try to manage existing Deployment Pods. That means you can safely update add Rollout
to the production environment without any interruption but you are going to run twice more Pods during migration.

**Traffic Management During Migration**

The Rollout offers traffic management functionality that manages routing rules and flows the traffic to different
versions of an application. For example [Blue-Green](../docs/features/bluegreen.md) deployment strategy manipulates
Kubernetes Service selector and direct production traffic to "green" instances only.

When migrating a Deployment which is already serving live production traffic, a Rollout should
run next to the Deployment before deleting the Deployment. **Not following this approach might result in
downtime**. It also allows for the Rollout to be tested before deleting the original Deployment.
If you are using this feature then Rollout switches production traffic to Pods that it manages. The switch happens
only when the required number of Pod is running and healthy so it is safe in production as well. However, if you
want to be extra careful then consider creating a temporal Service or Ingress object to validate Rollout behavior.
Once testing is done delete temporal Service/Ingress and switch rollout to production one.
39 changes: 39 additions & 0 deletions examples/rollout-template-ref.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/instance: rollout-canary
name: rollout-ref-deployment
spec:
replicas: 0
selector:
matchLabels:
app: rollout-ref-deployment
template:
metadata:
labels:
app: rollout-ref-deployment
spec:
containers:
- name: rollouts-demo
image: argoproj/rollouts-demo:blue
imagePullPolicy: Always
ports:
- containerPort: 8080
---
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: rollout-ref-deployment
spec:
replicas: 5
revisionHistoryLimit: 2
workloadRef:
apiVersion: apps/v1
kind: Deployment
name: rollout-ref-deployment
strategy:
canary:
steps:
- setWeight: 20
- pause: {duration: 10s}
Loading

0 comments on commit a634ec4

Please sign in to comment.