読者です 読者をやめる 読者になる 読者になる

めもめも

このブログに記載の内容は個人の見解であり、必ずしも所属組織の立場、戦略、意見を代表するものではありません。

OpenShift OriginによるDockerイメージ管理(3)〜Dockerfileからイメージを作成する際のお作法

はじめに

OpenShiftの環境では、Dockerイメージからコンテナを起動する際に(主にセキュリティ上の理由から)いくつかの制限がかけられるため、一定のお作法に従ってイメージを作成しておく必要があります。ここでは、そのようなイメージを作成して、OpenShiftの環境で実行する手順を紹介します。

(参考資料)
Fwd: openshift-nginx docker image running as non-root

nginxのイメージの例

特に大きな注意点として、コンテナ内のプロセスはrootユーザーでの実行が禁止されます。一般に、Dockerfileに「USER」指定がない場合、rootユーザーでプロセスが起動されますが、OpenShiftの環境ではこれが禁止されているために、コンテナの起動に失敗します。さらに、「USER」指定を行った場合でも、実際にプロセスを起動するUIDは、OpenShiftによって任意に選択されます。そのため、Dockerfileで作成するイメージは、「USER」指定を入れた上で、さらに、「任意の一般ユーザーで実行できる」ように設計しておく必要があります。具体的には、設定ファイルや実行バイナリーには、chmodコマンドで「ugo」に対するアクセス権を設定しておきます。

他には、細かい点ですが、「docker run」には「-d」オプションのみが指定される(「-it」オプションは指定されない)ため、仮想TTYを必要とするコマンド(bashなど)は実行できません。

以上のお作法にのっとった、nginxのDockerイメージを作成するDokcerfileの例が下記になります。

nginx with arbitrary non-root users

OpenShift OriginによるDockerイメージ管理(2)〜Dockerfileによるイメージビルドを自動化」で紹介した方法で、これからDockerイメージをビルドして、イメージストリームに登録しておきます。

nginx-sample_is.yml

apiVersion: v1
kind: ImageStream
metadata:
  name: nginx-sample

nginx-sample_bc.yml

apiVersion: v1
kind: BuildConfig
metadata:
  name: nginx-sample
spec:
  output:
    to:
      kind: ImageStreamTag
      name: nginx-sample:latest
  source:
    git:
      uri: https://github.com/enakai00/openshift_nginx_sample
    type: Git
  strategy:
    dockerStrategy:
      from:
        kind: ImageStreamTag
        name: centos7:latest
    type: Docker
  triggers:
  - imageChange: {}
    type: ImageChange
# oc login -u enakai
# oc delete project project01
# oc new-project project01 --description="Project to learn creating Docker images" --display-name="Learning Docker Image Development"

# docker pull centos:7
# docker login -u enakai -e enakai@example.com -p $(oc whoami -t) registry.oso.example.com
# docker tag docker.io/centos:7 registry.oso.example.com/project01/centos7:latest
# docker push registry.oso.example.com/project01/centos7:latest

# oc create -f nginx-sample_is.yml
# oc get is
NAME           DOCKER REPO                                TAGS      UPDATED
centos7        172.30.84.64:5000/project01/centos7        latest    31 seconds ago
nginx-sample   172.30.84.64:5000/project01/nginx-sample             

# oc create -f nginx-sample_bc.yml
# oc start-build nginx-sample
nginx-sample-1
# oc logs -f build/nginx-sample-1
...
I1229 23:38:38.193339       1 docker.go:86] Pushing image 172.30.84.64:5000/project01/nginx-sample:latest ...
I1229 23:39:18.956971       1 docker.go:90] Push successful

# oc describe bc nginx-sample
Name:           nginx-sample
Created:        About a minute ago
Labels:         <none>
Latest Version: 1
Strategy:       Docker
Source Type:    Git
URL:            https://github.com/enakai00/openshift_nginx_sample
From Image:     ImageStreamTag centos7:latest
Output to:      ImageStreamTag nginx-sample:latest
Triggered by:   ImageChange

Build           Status          Duration        Creation Time
nginx-sample-1  complete        1m7s            2015-12-30 08:38:16 +0900 JST

# oc describe is nginx-sample
Name:                   nginx-sample
Created:                3 minutes ago
Labels:                 <none>
Annotations:            openshift.io/image.dockerRepositoryCheck=2015-12-29T23:36:50Z
Docker Pull Spec:       172.30.84.64:5000/project01/nginx-sample

Tag     Spec            Created         PullSpec                                                                                                                Image
latest  <pushed>        50 seconds ago  172.30.84.64:5000/project01/nginx-sample@sha256:a570779c87525781651f80853890353e622b15be47daa1c3b90f027cff8a1392        

イメージストリームからコンテンを起動

イメージストリームに登録されたDockerイメージを使ってコンテナを起動するには、「デプロイ設定(DeploymentConfig)」を利用します。次が定義ファイルの例になります。

nginx-sample_dc.yml

apiVersion: v1
kind: DeploymentConfig
metadata:
  name: nginx-sample
spec:
  template:
    metadata:
      labels:
        name: nginx-sample
    spec:
      containers:
      - name: nginx-sample-latest
        image: nginx-sample:latest
        ports:
        - containerPort: 8080
          protocol: TCP
  replicas: 1
  selector:
    name: nginx-sample
  triggers:
  - type: ImageChange
    imageChangeParams:
      automatic: true
      containerNames:
      - nginx-sample-latest
      from:
        kind: ImageStreamTag
        name: nginx-sample:latest
  - type: ConfigChange

「template:」部分に起動するコンテナの情報(テンプレート)を記載します。「image:」の部分に使用するイメージを指定しますが、ここに記載した名称はダミーのものです。この部分は、「triggers:」によって、イメージストリームのタグで指定されるイメージで上書きされます。「triggers:」は、コンテナを再デプロイするトリガーを指定するもので、ここでは、「type: ImageChange」の「from:」部分でイメージストリームのタグ「nginx-sample:latest」が変化したら再デプロイするという指定を行っています。これにより、イメージストリームのタグで指定されるイメージからコンテナの起動が行われます。(これがないと、ダミー指定が上書きされず、Docker Hubの「nginx-sample:latest」を探しに行ってしまいます。)また、イメージの再ビルドによって、「nginx-sample:latest」が新しいイメージを指すと自動的に再デプロイが行われます。最後の「type: ConfigChange」は、デプロイ設定を変更したら自動で再デプロイするという指定です。これがあると、デプロイ設定を定義するとすぐにコンテナの起動が開始します。(デプロイ設定の定義そのものも、デプロイ設定の変更とみなされるためです。)

なお、「template:」に複数のコンテナを指定すると、これらは同一のPod(仮想NICを共有したコンテナ群)で起動します。「replicas:」と「selector:」は、指定のテンプレートから指定の数のPodを起動します。複数起動した場合は、自動でロードバランスが行われます。

それでは実際にデプロイ設定(dc)を投入します。

# oc create -f nginx-sample_dc.yml
# oc get dc
NAME           TRIGGERS                    LATEST
nginx-sample   ConfigChange, ImageChange   1

# oc describe dc nginx-sample
Name:           nginx-sample
Created:        10 seconds ago
Labels:         <none>
Latest Version: 1
Triggers:       Image(nginx-sample@latest, auto=true), Config
Strategy:       Rolling
Template:
  Selector:     name=nginx-sample
  Replicas:     1
  Containers:
  NAME                  IMAGE                                                                   ENV
  nginx-sample-latest   172.30.84.64:5000/project01/nginx-sample@sha256:a570779c87525781651f80853890353e622b15be47daa1c3b90f027cff8a1392        
Deployment #1 (latest):
        Name:           nginx-sample-1
        Created:        10 seconds ago
        Status:         Running
        Replicas:       1 current / 1 desired
        Selector:       deployment=nginx-sample-1,deploymentconfig=nginx-sample,name=nginx-sample
        Labels:         openshift.io/deployment-config.name=nginx-sample
        Pods Status:    0 Running / 1 Waiting / 0 Succeeded / 0 Failed
No events.

# oc get pod
NAME                    READY     STATUS      RESTARTS   AGE
nginx-sample-1-build    0/1       Completed   0          13m
nginx-sample-1-deploy   1/1       Running     0          17s
nginx-sample-1-omrw1    0/1       Pending     0          13s

「nginx-sample-1-deploy」は、デプロイ処理をキックするためのコンテナです。この中で、「dokcer pull」などの準備処理が行われます。「nginx-sample-1-omrw1」が実際に起動されるコンテナです。コンテナが無事に起動すると、「nginx-sample-1-deploy」は消滅します。

# oc get pods
NAME                   READY     STATUS      RESTARTS   AGE
nginx-sample-1-build   0/1       Completed   0          17m
nginx-sample-1-omrw1   1/1       Running     0          4m

次のコマンドで、起動したコンテナの詳細が確認できます。

# oc describe pod nginx-sample-1-omrw1

コンテナ内のアプリケーションへのアクセス

コンテナで起動したアプリケーションにアクセスするには、「サービス(Service)」と「ルーティング(Route)」を定義する必要が有ります。サービスは他のコンテナからのアクセスを許可するもので、ルーティングは外部ネットワークからのアクセスを許可するものになります。

まず、次の定義ファイルでサービス(svc)を設定します。

nginx-sample_svc.yml

apiVersion: v1
kind: Service
metadata:
  name: nginx-sample
spec:
  ports:
  - name: 8080-tcp
    protocol: TCP
    port: 8080
    targetPort: 8080
  selector:
    deploymentconfig: nginx-sample
# oc create -f nginx-sample_svc.yml 
# oc get svc
NAME           CLUSTER_IP      EXTERNAL_IP   PORT(S)    SELECTOR                        AGE
nginx-sample   172.30.137.89   <none>        8080/TCP   deploymentconfig=nginx-sample   5s

これで、他のコンテナからは、「172.30.137.89:8080」でアクセス可能になります。マスターからもこのIPで直接にアクセス可能です。

# curl http://172.30.137.89:8080
Hello, World!

続いて、次の定義ファイルでルーティング(route)を設定します。

nginx-sample_route.yml

apiVersion: v1
kind: Route
metadata:
  name: nginx-sample-route
spec:
  host: nginx-sample.project01.oso.example.com
  to:
    kind: Service
    name: nginx-sample
# oc create -f nginx-sample_route.yml 
# oc get route
NAME                 HOST/PORT                                PATH      SERVICE        LABELS    INSECURE POLICY   TLS TERMINATION
nginx-sample-route   nginx-sample.project01.oso.example.com             nginx-sample                               

これで外部からは、「http://nginx-sample.project01.oso.example.com」でアクセス可能になりました。

# curl http://nginx-sample.project01.oso.example.com
Hello, World!

なお、複数の定義をひとつのファイルにまとめることも可能です。「kind: List」を用いて、次のように記述します。

nginx-sample_access.yml

apiVersion: v1
kind: List
items:
- apiVersion: v1
  kind: Service
  metadata:
    name: nginx-sample
  spec:
    ports:
    - name: 8080-tcp
      protocol: TCP
      port: 8080
      targetPort: 8080
    selector:
      deploymentconfig: nginx-sample

- apiVersion: v1
  kind: Route
  metadata:
    name: nginx-sample-route
  spec:
    host: nginx-sample.project01.oso.example.com
    to:
      kind: Service
      name: nginx-sample

これを用いると、次のコマンドで、サービスとルーティングをまとめて設定することができます。

# oc create -f nginx-sample_access.yml

外部レジストリーのイメージを使用する方法

Docker Hubなどの外部レジストリーでも、OpenShiftで実行可能なDockerイメージが公開されています。これらを利用する場合、一度、内部レジストリーにPushしてイメージストリームに登録する他に、イメージストリームのタグ機能を用いて、イメージストリームから直接に外部レジストリーのイメージを参照することも可能です。

# oc tag --source=docker centos/postgresql-94-centos7:latest postgresql:9.4
# oc tag postgresql:9.4 postgresql:latest --alias
# oc describe is postgresql
Name:                   postgresql
Created:                11 seconds ago
Labels:                 <none>
Annotations:            openshift.io/image.dockerRepositoryCheck=2015-12-30T11:27:13Z
Docker Pull Spec:       172.30.84.64:5000/project01/postgresql

Tag     Spec                                    Created         PullSpec                                                                                                        Image
9.4     centos/postgresql-94-centos7:latest     9 seconds ago   docker.io/centos/postgresql-94-centos7@sha256:1ac41adb8b746bef67f289f86bcbed6afed50dd745e46371a562a4af8cb0b14c  
latest  postgresql:9.4                          9 seconds ago   docker.io/centos/postgresql-94-centos7@sha256:1ac41adb8b746bef67f289f86bcbed6afed50dd745e46371a562a4af8cb0b14c  

この例では、イメージストリーム「postgresql:latest」もしくは「postgresql:9.4」から、Docker Hub上のイメージ「centos/postgresql-94-centos7:latest」を参照することが可能になります。「--alias」オプションは、「postgresql:latest」は、常に「postgresql:9.4」と同じイメージを指すように指定します。