GeekFactory

int128.hatenablog.com

docker-gitbucketでExternal DBやPrefixなどを設定する

DockerでGitBucketを立ち上げる時は f99aq8ove/docker-gitbucket というイメージを利用しています。GitBucket最新版への追随が早いので重宝しています。

github.com

このたびPRがマージされたので、環境変数でExternal DBやPrefixなどを設定できるようになりました。例えば、下記のdocker-compose.ymlはPostgreSQLを利用します。

version: "2"

services:
  gitbucket:
    image: f99aq8ove/gitbucket
    environment:
      GITBUCKET_OPTS: --prefix=/gitbucket
      GITBUCKET_DB_URL: jdbc:postgresql://db/gitbucket
      GITBUCKET_DB_USER: gitbucket
      GITBUCKET_DB_PASSWORD: gitbucket
    links:
      - db
    volumes:
      - gitbucket-data:/gitbucket
  db:
    image: postgres:9.4
    environment:
      POSTGRES_USER: gitbucket
      POSTGRES_PASSWORD: gitbucket

volumes:
  gitbucket-data:
    driver: local

acmesmithでSSL証明書を管理する

開発環境などのテンポラリな環境でSSL証明書を利用したい場合はLet's Encryptが便利です。Let's Encryptの証明書を管理するツールにはcertbotlegoなどがありますが、それらは鍵や証明書をローカルディスクに格納するため、他のサーバで証明書を利用したい場合に鍵や証明書を安全に配布する仕組みが必要になります。また、発行済みの証明書が複数のサーバに分散してしまうと、有効期限やSAN(Subject Alternative Name)の更新に多大な手間がかかります。

そこで、acmesmithというツールを利用すると、S3などのストレージで鍵や証明書を一元管理できます。これにより、複数のサーバで証明書を利用する場合の運用がとても楽になります。

github.com

本稿では、acmesmithを利用して証明書を運用する方法を説明します。ここでは、Route53でドメインを管理しており、S3が利用できる前提とします。

準備

以下のIAMロールを持つIAMユーザを作成します。YOUR_BUCKETはS3のバケット名、YOUR_HOSTED_ZONEはRoute53のゾーンIDに置き換えてください。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject", "s3:ListBucket"],
      "Resource": ["arn:aws:s3:::YOUR_BUCKET", "arn:aws:s3:::YOUR_BUCKET/cert"]
    },
    {
      "Effect": "Allow",
      "Action": ["route53:ListHostedZones", "route53:GetChange"],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": "route53:ChangeResourceRecordSets",
      "Resource": ["arn:aws:route53:::hostedzone/YOUR_HOSTED_ZONE"]
    }
  ]
}

IAMキーを取得したら、以下の内容でacmesmith.ymlを作成します。YOUR_PASSPHRASEは鍵のパスフレーズなので適当なものに置き換えてください。

endpoint: https://acme-v01.api.letsencrypt.org/

storage:
  type: s3
  region: ap-northeast-1
  bucket: YOUR_BUCKET
  prefix: cert
  aws_access_key:
    access_key_id: IAM_KEY
    secret_access_key: IAM_SECRET

challenge_responders:
  - route53:
      aws_access_key:
        access_key_id: IAM_KEY
        secret_access_key: IAM_SECRET

account_key_passphrase: YOUR_PASSPHRASE1
certificate_key_passphrase: YOUR_PASSPHRASE2

acmesmithはRubyGemsで提供されています。

gem install acmesmith

初期登録と更新

まずは、Let's Encryptのアカウント登録と証明書発行を行います。ドメインの所有者であることの確認(ACME Challenge)はRoute53で行われます。

acmesmith register mailto:example@gmail.com
acmesmith authorize example.com
acmesmith request example.com
acmesmith list

証明書のリストが表示されれば成功です。S3にはLet's Encryptのアカウント鍵、SSL証明書とその秘密鍵が格納されます。

期限切れが近い証明書を更新するには以下を実行します。デフォルトでは期限切れ7日前を過ぎたら更新されます。そうでない場合は何もしないので、何回実行しても大丈夫です。

acmesmith autorenew

鍵や証明書はS3にあるので、IAMキーさえ設定すれば他のサーバでも証明書を取得できます。これもACMEサーバには問い合わせないので、何回実行しても大丈夫です。

acmesmith show-certificate example.com
acmesmith show-private-key example.com

Dockerで実行する

すでにWebサーバをDockerで運用している場合、acmesmithもDockerで実行するとよいでしょう。ここではRubyのイメージにacmesmithとacmesmith.ymlを追加します。

# acmesmith/Dockerfile
FROM ruby:2
RUN gem install acmesmith
ADD acmesmith.yml /
ENTRYPOINT ["acmesmith"]
# /docker-compose.yml
version: "2"
services:
  acmesmith:
    build: acmesmith

以下のコマンド群で証明書を生成できます。

docker-compose build acmesmith
docker-compose run --rm acmesmith autorenew
docker-compose run --rm acmesmith show-certificate example.com > cert.pem
docker-compose run --rm acmesmith show-private-key example.com > key.pem

生成された証明書をWebサーバに追加するとHTTPSが利用できるようになります。

証明書を更新する際にはイメージのリビルドとコンテナの再起動が必要になるため、およそ90日ごとに数秒のダウンタイムが発生します*1

最後に、acmesmithで生成した証明書をnginxで利用する例を公開します。

github.com

*1:Let's Encryptの用途を考えると、数秒のダウンタイムを許容できないケースは少ないかと思います。ダウンタイムを許容できない場合はボリューム経由で証明書を渡してシグナルを投げるといった工夫が必要です。

CoffeeScriptのアプリをDockerイメージにする

CoffeeScriptで書いたアプリをDockerイメージとしてリリースする方法を説明します。Dockerを使うとNode.jsのインストールや依存関係の解決を手動でやる必要がなくなるので、他の環境でさくっとアプリを実行したい時に便利です。

以下の簡単なスクリプトを例に説明します。スクリプトの名前は app.coffee とします。

console.log 'hoge'

スクリプトを実行するための package.json を作成します。この時、 npm startスクリプトが実行されるようにします。

{
  "name": "example",
  "version": "1.0.0",
  "description": "CoffeeScript app example",
  "repository": {
    "type": "git",
    "url": "https://github.com/int128/example.git"
  },
  "main": "app.coffee",
  "scripts": {
    "start": "node_modules/coffee-script/bin/coffee app.coffee"
  },
  "author": "int128",
  "license": "Apache-2.0"
}

最後に Dockerfile を書きます。

from node:onbuild

イメージをビルドして、コンテナを実行します。

% docker build -t int128/example .
% docker run --rm int128/example

簡単ですね!

参考までに、私が公開しているslack-dockerというツールでも本記事で紹介した方法を採用しています。Dockerコンテナの開始や終了のイベントをSlackに通知してくれるツールです。

github.com