Merge pull request #6793 from cybozu-go/ceph-fix-osd-corruption

ceph: avoid potential osd corruption due to wrong file-lock
This commit is contained in:
Satoru Takeuchi authored and GitHub committed 2021-01-20 09:39:39 +09:00
commit 0512c7d33c
4 files changed
+68 -24

No files matched your search

@@ -114,7 +114,7 @@ func (c *Cluster) provisionPodTemplateSpec(osdProps osdProperties, restart v1.Re
if osdProps.onPVC() {
// Create volume config for PVCs
volumes = append(volumes, getPVCOSDVolumes(&osdProps)...)
volumes = append(volumes, getPVCOSDVolumes(&osdProps, c.spec.DataDirHostPath, c.clusterInfo.Namespace, true)...)
if osdProps.encrypted {
// If a KMS is configured we populate
if c.spec.Security.KeyManagementService.IsEnabled() {
+22 -4
View File
@@ -20,7 +20,9 @@ package osd
import (
"fmt"
"path"
"path/filepath"
"strconv"
"strings"
"github.com/libopenstorage/secrets"
"github.com/pkg/errors"
@@ -227,6 +229,7 @@ var defaultTuneSlowSettings = map[string]string{
var cpArgs = []string{
"--archive", // Archive mode preserves the specified attributes
"--dereference", // always follow symbolic links in SOURCE to avoid broken links when copying dm devs
"--no-clobber", // do not overwrite an existing file
"--verbose",
}
@@ -268,7 +271,7 @@ func (c *Cluster) makeDeployment(osdProps osdProperties, osd OSDInfo, provisionC
// If the OSD runs on PVC
if osdProps.onPVC() {
// Create volume config for PVCs
volumes = append(volumes, getPVCOSDVolumes(&osdProps)...)
volumes = append(volumes, getPVCOSDVolumes(&osdProps, c.context.ConfigDir, c.clusterInfo.Namespace, false)...)
// If encrypted let's add the secret key mount path
if osdProps.encrypted && osd.CVMode == "raw" {
encryptedVol, _ := c.getEncryptionVolume(osdProps)
@@ -395,7 +398,7 @@ func (c *Cluster) makeDeployment(osdProps osdProperties, osd OSDInfo, provisionC
// so that it can pick the already mounted/activated osd metadata path
// This container will activate the OSD and place the activated filesinto an empty dir
// The empty dir will be shared by the "activate-osd" pod and the "osd" main pod
activateOSDVolume, activateOSDContainer := c.getActivateOSDInitContainer(osdID, osd, osdProps)
activateOSDVolume, activateOSDContainer := c.getActivateOSDInitContainer(c.context.ConfigDir, c.clusterInfo.Namespace, osdID, osd, osdProps)
if doActivateOSDInit {
volumes = append(volumes, activateOSDVolume)
volumeMounts = append(volumeMounts, activateOSDContainer.VolumeMounts[0])
@@ -645,8 +648,23 @@ func (c *Cluster) getCopyBinariesContainer() (v1.Volume, *v1.Container) {
}
// This container runs all the actions needed to activate an OSD before we can run the OSD process
func (c *Cluster) getActivateOSDInitContainer(osdID string, osdInfo OSDInfo, osdProps osdProperties) (v1.Volume, *v1.Container) {
volume := v1.Volume{Name: activateOSDVolumeName, VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}}
func (c *Cluster) getActivateOSDInitContainer(configDir, namespace, osdID string, osdInfo OSDInfo, osdProps osdProperties) (v1.Volume, *v1.Container) {
// We need to use hostPath because the same reason as written in the comment of getDataBridgeVolumeSource()
hostPathType := v1.HostPathDirectoryOrCreate
source := v1.VolumeSource{
HostPath: &v1.HostPathVolumeSource{
Path: filepath.Join(
configDir,
namespace,
strings.ReplaceAll(osdInfo.BlockPath, "/", "_"),
),
Type: &hostPathType,
},
}
volume := v1.Volume{
Name: activateOSDVolumeName,
VolumeSource: source,
}
envVars := osdActivateEnvVar()
osdStore := "--bluestore"
+32 -19
View File
@@ -19,6 +19,7 @@ package osd
import (
"fmt"
"path"
"path/filepath"
"github.com/libopenstorage/secrets"
kms "github.com/rook/rook/pkg/daemon/ceph/osd/kms"
@@ -86,7 +87,31 @@ func getDeviceMapperVolume() (v1.Volume, v1.VolumeMount) {
return volume, volumeMounts
}
func getPVCOSDVolumes(osdProps *osdProperties) []v1.Volume {
func getDataBridgeVolumeSource(claimName, configDir, namespace string, inProvisioning bool) v1.VolumeSource {
var source v1.VolumeSource
if inProvisioning {
source.EmptyDir = &v1.EmptyDirVolumeSource{
Medium: "Memory",
}
} else {
// We need to use hostPath to prevent multiple OSD pods from launching the same OSD and causing corruption.
// Ceph avoids this problem by locking fsid file and block device file under the data bridge volume directory.
// These locks are released by kernel once the process is gone, so until the ceph-osd daemon alives, the other
// pods (same OSD) will not be able to acquire them and will continue to be restarted.
// If we use emptyDir, this exclusive control doesn't work because the lock files aren't shared between OSD pods.
hostPathType := v1.HostPathDirectoryOrCreate
source.HostPath = &v1.HostPathVolumeSource{
Path: filepath.Join(
configDir,
namespace,
claimName),
Type: &hostPathType,
}
}
return source
}
func getPVCOSDVolumes(osdProps *osdProperties, configDir string, namespace string, prepare bool) []v1.Volume {
volumes := []v1.Volume{
{
Name: osdProps.pvc.ClaimName,
@@ -98,12 +123,8 @@ func getPVCOSDVolumes(osdProps *osdProperties) []v1.Volume {
// We need a bridge mount which is basically a common volume mount between the non privileged init container
// and the privileged provision container or osd daemon container
// The reason for this is mentioned in the comment for getPVCInitContainer() method
Name: fmt.Sprintf("%s-bridge", osdProps.pvc.ClaimName),
VolumeSource: v1.VolumeSource{
EmptyDir: &v1.EmptyDirVolumeSource{
Medium: "Memory",
},
},
Name: fmt.Sprintf("%s-bridge", osdProps.pvc.ClaimName),
VolumeSource: getDataBridgeVolumeSource(osdProps.pvc.ClaimName, configDir, namespace, prepare),
},
}
@@ -120,12 +141,8 @@ func getPVCOSDVolumes(osdProps *osdProperties) []v1.Volume {
// We need a bridge mount which is basically a common volume mount between the non privileged init container
// and the privileged provision container or osd daemon container
// The reason for this is mentioned in the comment for getPVCInitContainer() method
Name: fmt.Sprintf("%s-bridge", osdProps.metadataPVC.ClaimName),
VolumeSource: v1.VolumeSource{
EmptyDir: &v1.EmptyDirVolumeSource{
Medium: "Memory",
},
},
Name: fmt.Sprintf("%s-bridge", osdProps.metadataPVC.ClaimName),
VolumeSource: getDataBridgeVolumeSource(osdProps.metadataPVC.ClaimName, configDir, namespace, prepare),
},
}
@@ -145,12 +162,8 @@ func getPVCOSDVolumes(osdProps *osdProperties) []v1.Volume {
// We need a bridge mount which is basically a common volume mount between the non privileged init container
// and the privileged provision container or osd daemon container
// The reason for this is mentioned in the comment for getPVCInitContainer() method
Name: fmt.Sprintf("%s-bridge", osdProps.walPVC.ClaimName),
VolumeSource: v1.VolumeSource{
EmptyDir: &v1.EmptyDirVolumeSource{
Medium: "Memory",
},
},
Name: fmt.Sprintf("%s-bridge", osdProps.walPVC.ClaimName),
VolumeSource: getDataBridgeVolumeSource(osdProps.walPVC.ClaimName, configDir, namespace, prepare),
},
}
@@ -17,6 +17,7 @@ limitations under the License.
package osd
import (
"path/filepath"
"testing"
cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
@@ -44,3 +45,15 @@ func TestGetEncryptionVolume(t *testing.T) {
assert.Equal(t, v1.Volume{Name: "osd-encryption-key", VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{Medium: "Memory"}}}, v)
assert.Equal(t, v1.VolumeMount{Name: "osd-encryption-key", ReadOnly: false, MountPath: "/etc/ceph"}, vM)
}
func TestGetDataBridgeVolumeSource(t *testing.T) {
claimName := "test-claim"
configDir := "/var/lib/rook"
namespace := "rook-ceph"
source := getDataBridgeVolumeSource(claimName, configDir, namespace, true)
assert.Equal(t, v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{Medium: "Memory"}}, source)
hostPathType := v1.HostPathDirectoryOrCreate
source = getDataBridgeVolumeSource(claimName, configDir, namespace, false)
assert.Equal(t, v1.VolumeSource{HostPath: &v1.HostPathVolumeSource{Path: filepath.Join(configDir, namespace, claimName), Type: &hostPathType}}, source)
}