之前的這篇 「 使用 Amazon S3 作為 Maven 套件倉儲 」 文章中 , 山姆鍋提到使用 Amazon S3 作為套件倉儲的構想與好處 , 但那篇文章只適用于採用 Maven 作為建構工具的專案 。 山姆鍋已採用 Gradle 作為 「 影化身科技 」 的正式建構工具 , 所以 , 需要針對 Gradle 找到適合的方案 。

經過一番查訪 , 最終採用了本文所介紹的方案 , 雖然不是很理想 , 但尚可接受 。Gradle 將解析相依套件 (resolve dependencies) 跟發佈套件 (upload archives) 視為不同任務 , 不像 Maven 指定好套件倉儲就可以同時用於解析與發佈 。 所以 , 底下就分別說明 Gradle 如何發佈到 Amazon S3 以及使用它來解析相依套件 。

發佈建構套件

將建構好的套件發佈到 Amazon S3 仍舊需要第三方支援 ,Gradle 本身尚未有正式支援 。 下面是一個簡單 Java 專案的範例 build.gradle 檔 :

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'maven'

sourceCompatibility = 1.5
group = "com.eavatar"
version = '1.0'

jar {
    manifest {
        attributes 'Implementation-Title': 'Gradle Quickstart', 'Implementation-Version': version
    }
}

configurations {
    deployerJars
}

dependencies {
    compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
    testCompile group: 'junit', name: 'junit', version: '4.+'
    deployerJars "org.springframework.build.aws:org.springframework.build.aws.maven:3.0.0.RELEASE"
}


repositories {
    mavenCentral()
}


artifacts {
    archives jar
}

uploadArchives {
    configurations { deployerJars }
    dependencies {
        deployerJars "org.springframework.build.aws:org.springframework.build.aws.maven:3.0.0.RELEASE"
    }

    repositories.mavenDeployer { deployer ->
        def repositoryUrl = 's3://BUCKET/'

        description += repositoryUrl
        configuration = configurations.deployerJars
        s3credentials = [userName: 'S3_ACCESS_KEY',
                             passphrase: 'S3_SECRET_KEY']

        repository(url: repositoryUrl) { authentication(s3credentials) }
        snapshotRepository(url: repositoryUrl) { authentication(s3credentials) }
    }

}

其中 ,S3_ACCESS_KEY 跟 S3_SECRET_KEY 要換成您自己從 Amazon IAM 服務取得的 acess key id 以及 secret access key。BUCKET 則需要根據您的 S3 的 Bucket 名稱跟路徑修改 。 注意建立的這組 key pair 需要對該 S3 bucket 有寫入的權限 。 基於安全理由 , 最好也只對該 bucket 有權限 。

有了適當的設置後 , 就可以執行下列指令來發佈到 Amazon S3:

$ gradle uploadArchives

解析相依套件

Gradle 不支援直接以 S3 的協定來解析相依套件 , 也沒有適合的第三方插件可以使用 , 最好的情況也只是能從 S3 下載套件到本地 。 既然無法直接使用 S3 協定 , 只好轉個彎來解決問題 。 山姆鍋的解決方案利用 Amazon S3 可以將 Bucket 當作靜態網站的服務器的特性 , 讓整個套件倉儲的 bucket 可以 HTTP/HTTPS 協定存取 。 底下是範例用的 build.gradle 檔 :

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'maven'

sourceCompatibility = 1.5
group = "com.eavatar"
version = '1.1'

jar {
    manifest {
        attributes 'Implementation-Title': 'Gradle Quickstart', 'Implementation-Version': version
    }
}

dependencies {
    compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
    testCompile group: 'junit', name: 'junit', version: '4.+'
    compile 'com.eavatar:gradle-s3-test:1.1'
}

repositories {
    mavenCentral()
    maven {
        url 'http://S3_BUCKET/'
    }
}

artifacts {
    archives jar
}

由於 Amazon S3 當作網頁服務器並無法採用 IAM 的用戶認證 , 以上的設置需要開放給所有人存取 , 如果是開源軟體 , 基本上沒什麼問題 ; 但對於商業專案 , 應該是不能被接受的 , 這也是山姆鍋覺得這個做法不完善的地方 。 針對這點 , 山姆鍋建議可以下列方式加強 :

  • 限制能夠存取的 IP 位址 。
  • 將 Bucket 名稱以及路徑加入雜揍碼 (hash)。

限制 IP 位址會造成一些不方便 , 但如果一定需要確保正確的存取控制 , 這是目前山姆鍋建議的做法 。 在找尋方案的過程中 , 山姆鍋也發現了 S3Auth 這個服務可以讓您對存取進行 HTTP Basic 認證 , 由於不確定第三方服務是否可靠 , 避開了這個做法 。

底下是限制 S3 存取 IP 位址的 policy 設置範例 , 僅供參考 :

{
    "Version": "2012-10-17",
    "Id": "S3PolicyId1",
    "Statement": [
        {
            "Sid": "IPAllow",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*" 
            },
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::bucket/*",
            "Condition" : {
                "IpAddress" : {
                    "aws:SourceIp": "192.168.143.0/24" 
                },
                "NotIpAddress" : {
                    "aws:SourceIp": "192.168.143.188/32" 
                } 
            } 
        } 
    ]
}

小結

雖然有些不理想 , 但相信日後 Gradle 對於 Maven 現有專案的支援更完整後 , 這些問題應該可以解決 , 限制 IP 位址的作法則是目前可以接收的折衷方案 。