Wowza Community

S3FS Fuse-based file system

https://www.wowza.com/docs/how-to-use-the-fuse-based-file-system-backed-by-amazon-s3

http://s3fs.googlecode.com/files/s3fs-r188-source.tar.gz

Charlie

I love the s3fs concept and have been working with it to record audio without issue. I have created a script (called from startup.xml) that basically automounts an s3 bucket on startup, but the bucket doesn’t get remounted if I reboot the ec2 server. Interestingly, if I truncate the wowzamediaserverpro_startup.log file before the reboot, no script output happens on the reboot, which leads me to believe it doesn’t get run a second time.

Here is the startup.xml file:

<Startup>
 <Commands>
    <RunScript>
      <Script>tuning/mount_s3.sh</Script>
    </RunScript>
    <Install>
      <Folder>wowza</Folder>
    </Install>
    <RunScript>
      <Script>tuning/tune.sh</Script>
    </RunScript>
  </Commands>
</Startup>

Here is the mount_s3.sh file (excuse a couple of debugging echos in there):

#!/bin/sh
S3DIR="/mnt/s3"
S3BUCKET="bucket-name"
echo "Unmounting and removing the s3 recording bucket at $S3DIR if it exists"
echo "Check the contents of /mnt"
echo "`ls -l /mnt`"
if [ -d $S3DIR ]; then
  echo "ls -l $S3DIR"
  echo "`ls -l $S3DIR`"
  echo "/bin/fusermount -u $S3DIR"
  /bin/fusermount -u $S3DIR
else
  echo "$S3DIR doesn't exist"
fi
echo "rm -rf $S3DIR"
rm -rf $S3DIR
echo "Mounting the s3 recording bucket at $S3DIR"
echo "Creating /etc/passwd-s3fs"
echo "access_key:secret_key" > /etc/passwd-s3fs
echo "mkdir -p $S3DIR"
mkdir -p $S3DIR
echo "/usr/bin/s3fs $S3BUCKET $S3DIR"
/usr/bin/s3fs $S3BUCKET $S3DIR

You can use the variables. See the last part of the first post. It make take a bit of experimentation to get right.

Charlie

Make sense. We made this same change month and months ago for the vods3 stuff. I guess we will pick it up when it is integrated into the mainline. You can certainly build your own AMI now.

Charlie

It is a DIY project. I will give you a head start. Here is the base code:

package com.wowza.wms.module;
import java.io.*;
import java.util.*;
import com.wowza.util.*;
import com.wowza.wms.module.*;
import com.wowza.wms.application.*;
import com.wowza.wms.stream.*;
import com.wowza.wms.vhost.*;
public class ModuleMediaWriterFileMover extends ModuleBase implements IMediaWriterActionNotify
{
	String fileMoverDestinationPath = null;
	String fileMoverFileExtension = null;
	boolean fileMoverVersionFile = true;
	boolean fileMoverDeleteOriginal = false;
	
	public void onAppStart(IApplicationInstance appInstance)
	{
		appInstance.addMediaWriterListener(this);
		getLogger().info("ModuleMediaWriterFileMover.onAppStart: "+appInstance.getContextStr());
		
		WMSProperties props = appInstance.getProperties();
		
		fileMoverDestinationPath = props.getPropertyStr("fileMoverDestinationPath", fileMoverDestinationPath);
		fileMoverFileExtension = props.getPropertyStr("fileMoverFileExtension", fileMoverFileExtension);
		fileMoverVersionFile = props.getPropertyBoolean("fileMoverVersionFile", fileMoverVersionFile);
		fileMoverDeleteOriginal = props.getPropertyBoolean("fileMoverDeleteOriginal", fileMoverDeleteOriginal);
	}
	
	private String getExtension(IMediaStream stream, String ext)
	{
		if (ext == null)
		{
			String mediaReaderType = stream.getExt().toLowerCase();
			MediaReaderItem mediaReaderItem = stream.getStreams().getVHost().getMediaReaders().getMediaReaderDef(mediaReaderType);
			return mediaReaderItem.getFileExtension();
		}
		
		return ext;
	}
	public void onWriteComplete(IMediaStream stream, File file)
	{
		if (fileMoverDestinationPath != null)
		{
			String streamName = FileUtils.toValidFilename(stream.getName());
			String streamExt = getExtension(stream, fileMoverFileExtension);
			
			Map<String, String> envMap = new HashMap<String, String>();
			
			IApplicationInstance appInstance = stream.getStreams().getAppInstance();
			IVHost vhost = appInstance.getVHost();
			
			envMap.put("com.wowza.wms.context.VHost", vhost.getName());
			envMap.put("com.wowza.wms.context.VHostConfigHome", vhost.getHomePath());
			envMap.put("com.wowza.wms.context.Application", appInstance.getApplication().getName());
			envMap.put("com.wowza.wms.context.ApplicationInstance", appInstance.getName());
			envMap.put("com.wowza.wms.context.StreamName", streamName);
			
			String destinationPath =  SystemUtils.expandEnvironmentVariables(fileMoverDestinationPath, envMap);
			
			if (fileMoverDestinationPath.indexOf("com.wowza.wms.context.StreamName") < 0)
				destinationPath += "/"+streamName+"."+streamExt;
			
			File dstFile = new File(destinationPath);
			
			getLogger().info("ModuleMediaWriterFileMover.onWriteComplete: from: "+file+" to:"+dstFile);
	
			try
			{
				dstFile.getParentFile().mkdirs();
			}
			catch(Exception e)
			{
				getLogger().warn("ModuleMediaWriterFileMover.onWriteComplete[mkdir]: ["+dstFile+"]: "+e.toString());
			}
			
			try
			{
				if (dstFile.exists())
				{
					if (fileMoverVersionFile)
						FileUtils.versionFile(dstFile);
					else
						dstFile.delete();
				}
			}
			catch(Exception e)
			{
				getLogger().warn("ModuleMediaWriterFileMover.onWriteComplete[version]: ["+dstFile+"]: "+e.toString());
			}
			
			try
			{
				FileUtils.copyFile2(file, dstFile);
			}
			catch(Exception e)
			{
				getLogger().warn("ModuleMediaWriterFileMover.onWriteComplete[version]: ["+dstFile+"]: "+e.toString());
			}
	
			try
			{
				if (fileMoverDeleteOriginal && dstFile.exists())
				{
					file.delete();
				}
			}
			catch(Exception e)
			{
				getLogger().warn("ModuleMediaWriterFileMover.onWriteComplete[delOrig]: ["+dstFile+"]: "+e.toString());
			}
		}
	}
	
	public void onFLVAddMetadata(IMediaStream stream, Map<String, Object> extraMetadata)
	{
	}
}

Charlie

We do not have a method for sharing shard object between servers.

Charlie

I do not know if this will work. I have never tried it.

Charlie

Yes, EBS does not have this problem, it supposed to have better throughput then the instance local storage. That’s what Amazon says on their EBS site. And it is an excellent way to insulate yourself from instance failure.

Richard

Using ls command is the test, so if that’s not working something is not right.

Which AMI are you using? I used ami-ff8b6796 yesterday to try out the new ModuleMediaWriterFileMover, and it worked great.

Check your AWS keys.

Richarrd

Try it without the sub-bucket, and without the extra args. I assume you are replacing with your access and secret key.

Richard

It will not work to create a “sub-folder” with mkdir command from linux. It has to be a proper sub-bucket created in your S3 interface.

Richard

I tried it out, seems that sub-buckets or subfolders created with s3 plugin are useless. You can create different mount points. You’re not going to be able to do subfolders.

Richard

Thanks for the suggestion. I don’t think any other 3rd party tools are likely to be included anytime soon.

Richard

I don’t know why, but I think this solves the problem, it won’t affect the application significantly.

Take a look at this post:

http://www.wowza.com/forums/showthread.php?p=39370#5

Richard

About ftp created directories in s3. I don’t that’s a good idea. It’s like using mkdir on s3fs mounted bucket. It might look like it creates something, but it’s not usable. You have to use an S3 tool or EC2 tools to create buckets and “sub-buckets”

Richard

We only recommend s3fs for this one use case, copy file to s3 after recording:

http://www.wowza.com/community/t/-/235

Richard

We will look into it. I can’t provide a time frame, but it’s on the todo list now.

Richard

I suggest building ffmpeg and other such things on a EBS volume, which you can attach to a new instance and snapshot as necessary.

Richard