Merge branch 'master' into add-logical-backup

This commit is contained in:
Sergey Dudoladov 2019-03-05 14:07:48 +01:00
commit 825c5139c3
10 changed files with 72 additions and 33 deletions

View File

@ -212,6 +212,10 @@ configuration they are grouped under the `kubernetes` key.
class](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass)
that should be assigned to the Postgres pods. The priority class itself must be defined in advance.
Default is empty (use the default priority class).
* **master_pod_move_timeout**
The period of time to wait for the success of migration of master pods from an unschedulable node.
The migration includes Patroni switchovers to respective replicas on healthy nodes. The situation where master pods still exist on the old node after this timeout expires has to be fixed manually. The default is 20 minutes.
* **enable_pod_antiaffinity**
toggles [pod anti affinity](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/) on the Postgres pods, to avoid multiple pods

View File

@ -46,6 +46,7 @@ data:
pod_label_wait_timeout: 10m
ready_wait_interval: 3s
ready_wait_timeout: 30s
# master_pod_move_timeout: 10m
replication_username: standby
resource_check_interval: 3s
resource_check_timeout: 10m

View File

@ -58,10 +58,11 @@ type KubernetesMetaConfiguration struct {
// TODO: use a proper toleration structure?
PodToleration map[string]string `json:"toleration,omitempty"`
// TODO: use namespacedname
PodEnvironmentConfigMap string `json:"pod_environment_configmap,omitempty"`
PodPriorityClassName string `json:"pod_priority_class_name,omitempty"`
EnablePodAntiAffinity bool `json:"enable_pod_antiaffinity" default:"false"`
PodAntiAffinityTopologyKey string `name:"pod_antiaffinity_topology_key" default:"kubernetes.io/hostname"`
PodEnvironmentConfigMap string `json:"pod_environment_configmap,omitempty"`
PodPriorityClassName string `json:"pod_priority_class_name,omitempty"`
MasterPodMoveTimeout time.Duration `json:"master_pod_move_timeout,omitempty"`
EnablePodAntiAffinity bool `json:"enable_pod_antiaffinity" default:"false"`
PodAntiAffinityTopologyKey string `name:"pod_antiaffinity_topology_key" default:"kubernetes.io/hostname"`
}
// PostgresPodResourcesDefaults defines the spec of default resources

View File

@ -16,7 +16,7 @@ const (
var (
// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
// An instance of runtime.SchemeBuilder, global for this package
// SchemeBuilder : An instance of runtime.SchemeBuilder, global for this package
SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder
//AddToScheme is localSchemeBuilder.AddToScheme

View File

@ -561,6 +561,10 @@ func (c *Cluster) generateSpiloPodEnvVars(uid types.UID, spiloConfiguration stri
Name: "PAM_OAUTH2",
Value: c.OpConfig.PamConfiguration,
},
{
Name: "HUMAN_ROLE",
Value: c.OpConfig.PamRoleName,
},
}
if spiloConfiguration != "" {
envVars = append(envVars, v1.EnvVar{Name: "SPILO_CONFIGURATION", Value: spiloConfiguration})

View File

@ -91,7 +91,7 @@ func TestShmVolume(t *testing.T) {
podSpec: &v1.PodSpec{
Volumes: []v1.Volume{},
Containers: []v1.Container{
v1.Container{
{
VolumeMounts: []v1.VolumeMount{},
},
},
@ -101,11 +101,11 @@ func TestShmVolume(t *testing.T) {
{
subTest: "non empty PodSpec",
podSpec: &v1.PodSpec{
Volumes: []v1.Volume{v1.Volume{}},
Volumes: []v1.Volume{{}},
Containers: []v1.Container{
v1.Container{
{
VolumeMounts: []v1.VolumeMount{
v1.VolumeMount{},
{},
},
},
},

View File

@ -1,6 +1,10 @@
package controller
import (
"fmt"
"time"
"github.com/zalando/postgres-operator/pkg/util/retryutil"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
@ -38,6 +42,7 @@ func (c *Controller) nodeAdd(obj interface{}) {
}
c.logger.Debugf("new node has been added: %q (%s)", util.NameFromMeta(node.ObjectMeta), node.Spec.ProviderID)
// check if the node became not ready while the operator was down (otherwise we would have caught it in nodeUpdate)
if !c.nodeIsReady(node) {
c.moveMasterPodsOffNode(node)
@ -64,7 +69,9 @@ func (c *Controller) nodeUpdate(prev, cur interface{}) {
if !c.nodeIsReady(nodePrev) || c.nodeIsReady(nodeCur) {
return
}
c.moveMasterPodsOffNode(nodeCur)
}
func (c *Controller) nodeIsReady(node *v1.Node) bool {
@ -72,7 +79,7 @@ func (c *Controller) nodeIsReady(node *v1.Node) bool {
util.MapContains(node.Labels, map[string]string{"master": "true"}))
}
func (c *Controller) moveMasterPodsOffNode(node *v1.Node) {
func (c *Controller) attemptToMoveMasterPodsOffNode(node *v1.Node) error {
nodeName := util.NameFromMeta(node.ObjectMeta)
c.logger.Infof("moving pods: node %q became unschedulable and does not have a ready label: %q",
nodeName, c.opConfig.NodeReadinessLabel)
@ -83,7 +90,7 @@ func (c *Controller) moveMasterPodsOffNode(node *v1.Node) {
podList, err := c.KubeClient.Pods(c.opConfig.WatchedNamespace).List(opts)
if err != nil {
c.logger.Errorf("could not fetch list of the pods: %v", err)
return
return err
}
nodePods := make([]*v1.Pod, 0)
@ -148,9 +155,11 @@ func (c *Controller) moveMasterPodsOffNode(node *v1.Node) {
movedPods, totalPods, nodeName)
if leftPods := totalPods - movedPods; leftPods > 0 {
c.logger.Warnf("could not move master %d/%d pods from the %q node",
return fmt.Errorf("could not move master %d/%d pods from the %q node",
leftPods, totalPods, nodeName)
}
return nil
}
func (c *Controller) nodeDelete(obj interface{}) {
@ -161,3 +170,21 @@ func (c *Controller) nodeDelete(obj interface{}) {
c.logger.Debugf("node has been deleted: %q (%s)", util.NameFromMeta(node.ObjectMeta), node.Spec.ProviderID)
}
func (c *Controller) moveMasterPodsOffNode(node *v1.Node) {
err := retryutil.Retry(1*time.Minute, c.opConfig.MasterPodMoveTimeout,
func() (bool, error) {
err := c.attemptToMoveMasterPodsOffNode(node)
if err != nil {
return false, fmt.Errorf("unable to move master pods off the unschedulable node; will retry after delay of 1 minute")
}
return true, nil
},
)
if err != nil {
c.logger.Warning("failed to move master pods from the node %q: timeout of %v minutes expired", node.Name, c.opConfig.MasterPodMoveTimeout)
}
}

View File

@ -52,9 +52,10 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
result.ClusterNameLabel = fromCRD.Kubernetes.ClusterNameLabel
result.NodeReadinessLabel = fromCRD.Kubernetes.NodeReadinessLabel
result.PodPriorityClassName = fromCRD.Kubernetes.PodPriorityClassName
result.MasterPodMoveTimeout = fromCRD.Kubernetes.MasterPodMoveTimeout
result.EnablePodAntiAffinity = fromCRD.Kubernetes.EnablePodAntiAffinity;
result.PodAntiAffinityTopologyKey = fromCRD.Kubernetes.PodAntiAffinityTopologyKey;
result.EnablePodAntiAffinity = fromCRD.Kubernetes.EnablePodAntiAffinity
result.PodAntiAffinityTopologyKey = fromCRD.Kubernetes.PodAntiAffinityTopologyKey
result.DefaultCPURequest = fromCRD.PostgresPodResources.DefaultCPURequest
result.DefaultMemoryRequest = fromCRD.PostgresPodResources.DefaultMemoryRequest

View File

@ -88,24 +88,25 @@ type Config struct {
// default name `operator` enables backward compatibility with the older ServiceAccountName field
PodServiceAccountName string `name:"pod_service_account_name" default:"operator"`
// value of this string must be valid JSON or YAML; see initPodServiceAccount
PodServiceAccountDefinition string `name:"pod_service_account_definition" default:""`
PodServiceAccountRoleBindingDefinition string `name:"pod_service_account_role_binding_definition" default:""`
DbHostedZone string `name:"db_hosted_zone" default:"db.example.com"`
AWSRegion string `name:"aws_region" default:"eu-central-1"`
WALES3Bucket string `name:"wal_s3_bucket"`
LogS3Bucket string `name:"log_s3_bucket"`
KubeIAMRole string `name:"kube_iam_role"`
DebugLogging bool `name:"debug_logging" default:"true"`
EnableDBAccess bool `name:"enable_database_access" default:"true"`
EnableTeamsAPI bool `name:"enable_teams_api" default:"true"`
EnableTeamSuperuser bool `name:"enable_team_superuser" default:"false"`
TeamAdminRole string `name:"team_admin_role" default:"admin"`
EnableAdminRoleForUsers bool `name:"enable_admin_role_for_users" default:"true"`
EnableMasterLoadBalancer bool `name:"enable_master_load_balancer" default:"true"`
EnableReplicaLoadBalancer bool `name:"enable_replica_load_balancer" default:"false"`
CustomServiceAnnotations map[string]string `name:"custom_service_annotations"`
EnablePodAntiAffinity bool `name:"enable_pod_antiaffinity" default:"false"`
PodAntiAffinityTopologyKey string `name:"pod_antiaffinity_topology_key" default:"kubernetes.io/hostname"`
PodServiceAccountDefinition string `name:"pod_service_account_definition" default:""`
PodServiceAccountRoleBindingDefinition string `name:"pod_service_account_role_binding_definition" default:""`
MasterPodMoveTimeout time.Duration `name:"master_pod_move_timeout" default:"20m"`
DbHostedZone string `name:"db_hosted_zone" default:"db.example.com"`
AWSRegion string `name:"aws_region" default:"eu-central-1"`
WALES3Bucket string `name:"wal_s3_bucket"`
LogS3Bucket string `name:"log_s3_bucket"`
KubeIAMRole string `name:"kube_iam_role"`
DebugLogging bool `name:"debug_logging" default:"true"`
EnableDBAccess bool `name:"enable_database_access" default:"true"`
EnableTeamsAPI bool `name:"enable_teams_api" default:"true"`
EnableTeamSuperuser bool `name:"enable_team_superuser" default:"false"`
TeamAdminRole string `name:"team_admin_role" default:"admin"`
EnableAdminRoleForUsers bool `name:"enable_admin_role_for_users" default:"true"`
EnableMasterLoadBalancer bool `name:"enable_master_load_balancer" default:"true"`
EnableReplicaLoadBalancer bool `name:"enable_replica_load_balancer" default:"false"`
CustomServiceAnnotations map[string]string `name:"custom_service_annotations"`
EnablePodAntiAffinity bool `name:"enable_pod_antiaffinity" default:"false"`
PodAntiAffinityTopologyKey string `name:"pod_antiaffinity_topology_key" default:"kubernetes.io/hostname"`
// deprecated and kept for backward compatibility
EnableLoadBalancer *bool `name:"enable_load_balancer"`
MasterDNSNameFormat StringTemplate `name:"master_dns_name_format" default:"{cluster}.{team}.{hostedzone}"`

View File

@ -130,7 +130,7 @@ func Coalesce(val, defaultVal string) string {
return val
}
// RequestIsSmallerThanLimit
// RequestIsSmallerThanLimit : ...
func RequestIsSmallerThanLimit(requestStr, limitStr string) (bool, error) {
request, err := resource.ParseQuantity(requestStr)