[패스워드 적용]
vi /etc/login.defs
PASS_MAX_DAYS   70
PASS_MIN_DAYS   0
PASS_MIN_LEN    8
PASS_WARN_AGE   7

[Umask]
sudo vi /etc/login.defs
UMASK 022
또는 주석처리


[JBoss sudo적용]
vi /etc/sudoers
jboss   ALL=(ALL)       ALL

usermod -g root jboss

[불필요 서비스 제거]
chkconfig --list | egrep "bootp|chargen|cmsd|daytime|discard|echo|finger|netstat|rusersd|sprayed|systat|tftp|time|ttdbserverd|uucp|apmd|bluetooth|cups|gpm|isdn|netfs|nfslock|portmap|sendmail"

chkconfig bluetooth off
chkconfig cups off
chkconfig gpm off
chkconfig isdn off
chkconfig nfslock off
chkconfig sendmail    off

[시스템 파일 권한 변경]
chmod 750 $(which lastlog)
chmod 750 $(which last)
chmod 750 $(which lastb)
chmod 750 $(which w)
chmod 750 $(which who)
chmod 750 $(which users)
chmod 750 $(which finger)
chmod 750 $(which dmesg)
chmod 750 $(which ifconfig)
chmod 4750 $(which sudo)

[ssh 3분설정]
vi /etc/ssh/sshd_config
ClientAliveInterval 180

[로그인시 보안 문구 변경]

vim /etc/motd
크리에이티브 커먼즈 라이센스
Creative Commons License
2012/02/24 14:11 2012/02/24 14:11

rds-modify-db-parameter-group rdsGroup -p "name=join_buffer_size, value=8388608, method=immediate" --aws-credential-file /usr/local/amazon/credential-file-path.template
rds-modify-db-parameter-group rdsGroup -p "name=sort_buffer_size, value=8388608, method=immediate" --aws-credential-file /usr/local/amazon/credential-file-path.template
rds-modify-db-parameter-group rdsGroup -p "name=read_rnd_buffer_size, value=16777216, method=immediate" --aws-credential-file /usr/local/amazon/credential-file-path.template
rds-modify-db-parameter-group rdsGroup -p "name=read_buffer_size, value=2091008, method=immediate" --aws-credential-file /usr/local/amazon/credential-file-path.template
rds-modify-db-parameter-group rdsGroup -p "name=max_heap_table_size, value=67108864, method=immediate" --aws-credential-file /usr/local/amazon/credential-file-path.template
rds-modify-db-parameter-group rdsGroup -p "name=table_open_cache, value=1024, method=immediate" --aws-credential-file /usr/local/amazon/credential-file-path.template
크리에이티브 커먼즈 라이센스
Creative Commons License
2012/02/24 08:52 2012/02/24 08:52

rds-modify-db-parameter-group rdsGroup -p "name=query_cache_size, value=67108864, method=immediate" --aws-credential-file /usr/local/amazon/credential-file-path.template

rds-modify-db-parameter-group rdsGroup -p "name=query_cache_limit, value=2097152, method=immediate" --aws-credential-file /usr/local/amazon/credential-file-path.template
크리에이티브 커먼즈 라이센스
Creative Commons License
2012/02/24 08:51 2012/02/24 08:51

Amazon ELB를 사용하기 위해서는 private key의 패스워드가 걸려있지 않아야 등록이 됩니다.

private key 패스워드 설정을 할 경우 웹서버 기동을 위해 패스워드를 입력해야 합니다.
 
상식적으로 생각해 볼 경우 ELB입장에서 사용자가 임의대로 걸어놓은 인증서의 패스워드를 인식하지 못할 것입니다. 결론적으로, 당연히 패스워드가 없는 private key를 첨부드린 이미지처럼 요구할 것으로 예상됩니다.


한국전자인증에서 도메인에 대한 인증서를 만들어 받았는데, ELB등록시 Cert부분에서 아래와 같은 메시지가 나옵니다.

Error: Private key must not be encrypted with a passphrase. 


위를 해결하기 위해서는 private key에 passphrase를 지워야 합니다.

1. openssl을 설치합니다.
$> yum install openssl

2. 패스워드를 제거합니다.
$> openssl rsa -in private.key -out private.key.insecure
$> mv private.key private.key.secure
$> mv private.key.insecure private.key

3. key를 등록합니다.
크리에이티브 커먼즈 라이센스
Creative Commons License
2012/02/22 17:30 2012/02/22 17:30

EC2 인스턴스의 서버 유형은 아마존 콘솔에서는 변경이 불가능합니다.
CLI를 이용하여야 하며, 자신 계정의 Security Credential에서 X.509의 private-key와 certicate을 다운받은 후 다음의 명령으로 가능합니다.

ec2 tool은 반드시 설치되어 있어야 합니다.

 ec2-modify-instance-attribute --debug  -C cert-yours.pem -K pk-yours.pem --instance-type m1.large --region eu-west-1 $INSTANCE_ID

크리에이티브 커먼즈 라이센스
Creative Commons License
2012/02/21 20:05 2012/02/21 20:05

Amazon EC2의 제한점이라면 EC2 인스턴스의 동일본이 다른 지역으로 복사가 안된다는 점입니다.

아래와 같이 여러 많은 방법들이 소개되고 있다. 스크립트도 쓰고, 별의 별 방법을 다 동원하고 있지만 접근하기는 쉽지가 않습니다.

http://alestic.com/2010/10/ec2-ami-copy 

http://blog.ibd.com/scalable-deployment/copy-an-ebs-ami-image-to-another-amazon-ec2-region/ 


간단한 방법으로 인스턴스의 이미지를 다른 지역으로 복사하는 방법을 설명합니다.

1. 먼저 현재 EBS 또는 S3 형태의 볼륨보다 큰 EBS 볼륨을 관리콘솔에서 생성하여 복사할 EC2인스턴스에 attach시킵니다.

[root@ip-10-224-114-237 ~]# df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/sda1              30G  4.0G   25G  15% /
tmpfs                 308M     0  308M   0% /dev/shm


위의 파일 시스템을 보면 30G 짜리의 /dev/sda1으로 root가 mount되어 있습니다.

2. 위의 루프파일 시스템보다 큰 크기인 40G 정도의 볼륨을 생성하여 마운트 시킵니다.
$> mkfs -t ext3 /dev/sdg
$> mkdir -p /mnt/sdg
$> mount -t ext3 /dev/sdg /mnt/sdg

3. 마운트가 끝나면 현재의 루프파일 시스템을 dd명령을 이용하여 mnt/sdg에 생성한 후 압축합니다.
$> dd if=/dev/sda1 of=/mnt/sdg/template bs=10M
$> tar -zcvf template.tar.gz /mnt/sdg/template

4. 아마존 콘솔을 이용하여 대상지역(target region)에 EC2 인스턴스를 micro로 생성합니다. 생성 후 위의 /dev/sda1과 동일한 크기의 EBS를 생성하여 신규 시스템에 attach 시킵니다.
위의 예에서는 30G짜리의 EBS를 사용했으므로 동일하게 30G짜리를 생성합니다.
$> mkfs -t ext3 /dev/sdg
$> mkdir -p /mnt/sdg
$> mount -t ext3 /dev/sdg /mnt/sdg


5. Source측의 압축된 template.tar.gz파일을 #4에서 생성된 인스턴스로 scp를 사용하여 전송합니다.
$> chmod 600 target-region.pem
$> scp -i target-region.pem  template.tar.gz root@ec2-176-34-228-119.eu-west-1.compute.amazonaws.com:~/


6. 대상지역에서 template.tar.gz의 압축을 푼 후, dd명령을 이용하여 attach된 볼륨에 template을 복제합니다.
$> tar -zxvf template.tar.gz
#> dd if=template of=/dev/sdg bs=10M

7. 위의 /dev/sdg의 마운트를 해제합니다.
$> umount /dev/sdg

8. 아마존 콘솔에서 해당 EBS 볼륨에 대한 스냅샷을 생성합니다.
아마존 콘솔(Amazon Console) -> Volume -> Create snapshot

9. EC2 command line tool을 이용하여 해당 스냅샷을 이용한 인스턴스를 생성합니다.
linux-box-source:~/ec2-api-tools-1.4.2.4$ ./bin/ec2-register --private-key pk-your.pem --cert cert-your.pem -v -H --region eu-west-1 -a  x86_64 -s snap-a485f7cc  -d 'CopyAMI Generated' -n 'AMI_DESCRIPTION' --root-device-name /dev/sda1 --kernel aki-5f7f552b


명령어 입력전에 ec2-api-tool이 반드시 설치되어 있어야 한다. 커널 아이디 'aki-5f7f552b'는 OEL5.5의 기본 커널 아이디이며, snap-a485f7cc 는 위에서 생성된 스냅샷, 대상 지역은 복사하고자 하는 위치입니다.


위의 private-key와 cert-key는 Amazon Console의 오른쪽 위의 Security Credentials의 CREDENTIALS의 X.509 탭을 누르면 나오는 "CREATE A NEW CERTIFICATE" 버튼을 눌러 나타나는 키를 다운받습니다.

10. 생성이 완료되면 아마존 콘솔에서 대상지역의 AMI를 보면 방금 생성한 AMI가 보일것이다.
생성된 AMI를 이용하여 인스턴스를 launch합니다.

11. 아마존 인스턴스의 경우 swap은 복사되지 않으므로 새로운 swap volume을 생성하여 인스턴스에 attach한 후 스왑을 만들어줘야 합니다.

fdisk /dev/sdf
          Command (m for help): n
          Command action
             e   extended
             p   primary partition (1-4)
          p
          Partition number (1-4): 1
          First cylinder (1-1044, default 1):
          Last cylinder or +size or +sizeM or +sizeK (1-1044, default 1044):
         
          Command (m for help): t
          Selected partition 1
          Hex code (type L to list codes): 82
          Changed system type of partition 1 to 82 (Linux swap / Solaris)
         
          Command (m for help): w
          The partition table has been altered!
         
          Calling ioctl() to re-read partition table.
          Syncing disks.
> mkswap /dev/sdf1

/etc/fstab을 편집하여 다음을 추가합니다.
[root@ip-10-124-189-22 ~]# cat /etc/fstab
/dev/sda1                 /                       ext3    defaults        1 1
tmpfs                   /dev/shm                tmpfs   defaults        0 0
devpts                  /dev/pts                devpts  gid=5,mode=620  0 0
sysfs                   /sys                    sysfs   defaults        0 0
proc                    /proc                   proc    defaults        0 0
/dev/sdf1               none                    swap    sw,nobootwait   0 0



12. 모든 것이 완료되었다. template ami를 생성하기 위해 아래의 명령을 실행하고 create ami로 해당 지역의 템플릿을 생성합니다.
chmod a+x /etc/rc.d/ec2.sh
 cat /dev/null > /var/log/boot.log
 cat /dev/null > /var/log/cron.log
 cat /dev/null > /var/log/dmesg.log
 cat /dev/null > /var/log/maillog.log
 cat /dev/null > /var/log/messages.log
 cat /dev/null > /var/log/oraclevm-template.log
 cat /dev/null > /var/log/secure
 cat /dev/null > /var/log/audit/audit.log
 cat /dev/null > /var/log/cups/error_log
 cat /dev/null > /root/.bash_history
 history -c


끝.
크리에이티브 커먼즈 라이센스
Creative Commons License
2012/02/21 10:58 2012/02/21 10:58

아마존 SQS를 사용하여 light weight ETL을 만들었습니다.
요건 중의 하나는 설정에 의해 소스에서 조회된 데이터베이스의 내용을 그대로 대상 시스템의 데이터베이스로 입력하는 작업이었습니다.

쿼리는 테이블의 구조에 따라 동적으로 생성되어야 하며, 한 번 생성된 쿼리는 MyBatis MappedStatement에 등록된 후 메모리에서 재사용되어야 하는 조건이 있습니다.

순서는 다음과 같습니다.
1. 추출(Extract)될 데이터베이스 테이블의 키값이 들어오면 해당 키를 이용하여 테이블을 조회한다.
2. 조회시 데이터베이스 메타데이터로 컬럼 타입을 같이 조회한 후 특정 데이터객체에 컬럼이름, 값, 컬럼타입, 테이블명을 같이 포함시킨다.
3. 데이터를 SQS로 보낸다. 단 조건은 Base64로 인코딩되어지며, 아마존 SQS의 한계인 64K가 넘어갈 경우 압축을 시도하고 압축된 내용이 다시 64K를 넘게 되면 분할을 시도한다.
4. 수신시 입력된 Base64를 디코딩한 후 JSON데이터를 객체로 변환시킨다.
5. 수신된 데이터의 객체를 이용하여 요청유형에 따라 UPSERT, INSERT, DELTE, UPDATE 구문을 만든다.
6. 데이터를 MyBatis로 전달하여 처리한다.

보통 웹 애플리케이션을 작성할 경우 MyBatis의 Mapper interface를 아래와 같이 미리 작성하거나, xml파일을 사용하여 미리 정의된 쿼리를 사용합니다.

/**
	 * Insert log data to master table
	 * @param master
	 */
	@Insert(INSERT_MASTER)
	public void insertMaster(LogMaster master);

	/**
	 * Update log data to master table
	 * @param master
	 */
	@Update(UPDATE_MASTER)
	public void updateMaster(LogMaster master);

	/**
	 * Insert log data to datail table
	 * @param detail
	 */
	@Insert(INSERT_DETAIL)
	public void insertDetail(LogDetail detail);

	@Select(SELECT_BUSINESS)
	public List<LogBusiness> getAllBusiness();


위의 조건 중 #5번처럼 동적으로 PreparedStatement 쿼리를 만들 경우에는 이야기가 좀 달라져서 런타임시에 그 값을 적용해야 합니다.

아래의 코드는 쿼리는 MyBatis의 MappedStaement로 저장하는 예제입니다.

import javax.annotation.PostConstruct;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.ibatis.builder.xml.dynamic.DynamicSqlSource;
import org.apache.ibatis.builder.xml.dynamic.SqlNode;
import org.apache.ibatis.builder.xml.dynamic.TextSqlNode;
import org.apache.ibatis.datasource.pooled.PooledDataSource;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.session.AutoMappingBehavior;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.apache.log4j.Logger;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class TargetSessionFactory {
	private final Logger logger = Logger.getLogger(this.getClass());
	
	private @Autowired @Qualifier("tgtDataSource") BasicDataSource dataSourceProp;
	private SqlSessionFactory factory;
	private Configuration configuration;
	private PooledDataSource dataSource;
	
	private Map<String, String> queryMap = new HashMap<String, String>();
	
	/**
	 * Create MyBatis Session Factory to load data.
	 */
	@PostConstruct
	public void createSessionFactory() {
		dataSource = new PooledDataSource(dataSourceProp.getDriverClassName(), 
													dataSourceProp.getUrl(),
													dataSourceProp.getUsername(),
													dataSourceProp.getPassword());
		dataSource.setPoolMaximumActiveConnections(dataSourceProp.getMaxActive());
		
		TransactionFactory transactionFactory = new JdbcTransactionFactory();
		Environment environment = new Environment( "athena", transactionFactory, dataSource );
		
		configuration = new Configuration(environment);
		configuration.setLazyLoadingEnabled(false);
		configuration.setAutoMappingBehavior(AutoMappingBehavior.FULL);

		SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
		factory = builder.build(configuration);
	}
	
	/**
	 * Open MyBatis session
	 * @return opend sql session.
	 * @throws SQLException
	 */
	public SqlSession openSession() throws SQLException {
		Connection conn = dataSource.getConnection();
		SqlSession session = factory.openSession(conn);
		return session;
	}
	
	/**
	 * Close session
	 * @param session
	 */
	public void closeSession(SqlSession session) {
		session.close();
	}

	/**
	 * Add query to MyBatis mapped statement. 
	 * @param query
	 */
	public void addMappedStatement(String query) {
		if( queryMap.containsKey(query) ) {
			logger.debug(">>>>>>>>>>>>>>>>>> Dynamic Query is exist in mapped statements");
			return;
		}
		
		SqlNode rootSqlNode = new TextSqlNode(query);
		SqlSource sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
		
		MappedStatement.Builder msb = new MappedStatement.Builder(configuration, query, sqlSource, SqlCommandType.INSERT );
		configuration.addMappedStatement(msb.build());
		
		logger.debug("================= [Mapped Statement ]=====================");
		logger.debug(configuration.getMappedStatementNames());
		logger.debug("================= [End Mapped Statement ]=====================");
		queryMap.put(query, query);
	}
}



위의 코드를 사욯하면 MyBatis의 쿼리를 동적으로 등록할 수 있습니다.
크리에이티브 커먼즈 라이센스
Creative Commons License
2012/02/20 13:49 2012/02/20 13:49

Unjar/Unzip/Unwar는 모두 Expand를 쓰고 있었는데 해당 task가 없어서 한참을 찾아헤맸다는..

package com.athena.cloud.provisioning.common.ant; import java.io.File; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Expand; import org.apache.tools.ant.taskdefs.Jar; import org.apache.tools.ant.taskdefs.Manifest; import org.apache.tools.ant.taskdefs.Manifest.Attribute; import org.apache.tools.ant.taskdefs.ManifestException; import org.apache.tools.ant.types.FileSet; import com.athena.cloud.provisioning.common.SystemConstants; import com.athena.cloud.provisioning.common.SystemProperties; @Service public class JarService { private @Autowired SystemProperties prop; /** * 입력된 디렉토리를 이용하여 압축 파일을 만든다. * @param jarName * @param directoryName * @throws ManifestException */ public void compressDirectory(String jarName, String directoryName, Map<String, String> attributes) throws ManifestException { Project project = new Project(); String repositoryDir = prop.get(SystemConstants.REPOSITORY_URL); File resultJarFile = new File(repositoryDir, jarName); File filesetDir = new File(directoryName); Jar jar = new Jar(); jar.setProject(project); // Set fileset information jar.setDestFile(resultJarFile); FileSet fileSet = new FileSet(); fileSet.setDir(filesetDir); jar.addFileset(fileSet); // Create manifest Manifest manifest = new Manifest();         for( Map.Entry<String, String> entry : attributes.entrySet() ){         Attribute attribute = new Attribute();         attribute.setName(entry.getKey());         attribute.setValue(entry.getValue());         manifest.addConfiguredAttribute(attribute);         }         jar.addConfiguredManifest(manifest); jar.execute(); } /** * 입력된 디렉토리를 이용하여 압축 파일을 만든다. * @param jarName * @param directoryName * @throws ManifestException */ public void decompressAsi(String asiName, String targetDir) { Project project = new Project(); String repositoryDir = prop.get(SystemConstants.REPOSITORY_URL); File src = new File(repositoryDir, asiName); File dest = new File(targetDir); Expand zip = new Expand(); zip.setProject(project); // Set fileset information zip.setSrc(src); zip.setDest(dest); zip.execute(); } }
크리에이티브 커먼즈 라이센스
Creative Commons License
2012/02/17 15:09 2012/02/17 15:09


10G -> 100G


$ ec2-run-instances ami-599a4b30 --region us-east-1 -n 10 -g athena-sg-common -k dev-key-us -t m1.xlarge --availability-zone us-east-1a -b /dev/sda1=:100:true

(새 ec2로 로그인)
$ su -
$ resize2fs /dev/sda1
크리에이티브 커먼즈 라이센스
Creative Commons License
2012/02/16 14:09 2012/02/16 14:09

What are the killer features in AS 7 for operations people  ?

To get the ball rolling :
  • Super, powerful command line for scripting / power users
  • Fast, responsive web console for learning / occasional users
  • Stable, remotable management API
  • Domain Management (multi-machine management, deployment, etc.)
  • Fast boot / reboot (less down-time during rolling upgrades, outages)
  • Simple, consistent configuration
  • Simplified clustering
  • Common log format, unique log-ids
  • Simplified security architecture
  • Separation of binaries and configuration
  • Definition of public and private APIs
  • Localized / Internationalized
  • Powerful modular class-loader

That's just scratching the surface
크리에이티브 커먼즈 라이센스
Creative Commons License
2012/02/15 09:49 2012/02/15 09:49