Developing Environment with docker-compose & alpine 2017


About this article

This article is renewal version of Docker Compose + alpineでサーバー一式を構築する1.

I have to work with Chinese people. I'm not good at English and I don't know Chinese at all. So it's just challenge for me.

If you find any mistakes, not only language but also contents, please feel free to comment me.

Abstraction

Setup VPS as docker host and build servers below

  • dns
  • http reverse proxy
  • gitbucket
  • jenkins

VPS

I use ConoHa VPS service by GMO Japan. http://conoha.jp/en
ConoHa charges even if instance is down but it's still cheap(Mem1GB 900JPY/month). It's not famous in other country. There're not many informations in English. If you are familiar to other VPS, you should use others.

change keymap of VM

If I had a time I wanna use AWS. But infra is not my main task. I just wanna reduce study time to acquire more study time for programming environment.

Change OS keymap

echo loadkeys us >> .bashrc

Update packages

apt-get update && apt-get upgrade
Install your locale files (ex: Japanese)
apt-get install language-pack-ja-base

SSH public key

Add your key to ~/.ssh/authorized_keys
Try to login without password.

Install docker-compose

curl -L https://github.com/docker/compose/releases/download/1.12.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

dns

docker-compose.yml
dns:
  container_name: dns
  build: dns
  volumes:
    - "./dns/bind:/etc/bind"
  ports:
    - "53:53"
    - "53:53/udp"

build <name>: search Docker file from ./<name> directory

mkdir dns

vi dns/Dockerfile

dns/Dockerfile
FROM alpine:latest
RUN apk --update add bind && rm -rf /var/cache/apk/*
EXPOSE 53
CMD ["named", "-c", "/etc/bind/named.conf", "-g", "-u", "named"]

[1]

Just install bind and mount config files from host's ./dns/bind directory.

dns/bind/named.conf
options {
        directory "/var/bind";
        allow-transfer {
                none;
        };
        pid-file "/var/run/named/named.pid";
        allow-recursion { none; };
        recursion no;
};

zone "proj.example.jp" IN {
    type master;
    file "/etc/bind/db.example";
};
dns/bind/db.example
; BIND data file for proj.example.jp
$TTL    604800
@       IN      SOA     dns.example.jp. root.localhost. (
                       20170401         ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                         604800 )       ; Negative Cache TTL
;
@       IN      NS      dns.example.jp.
@       IN      A       127.0.0.1
@       IN      AAAA    ::1
dev IN      A       172.17.0.1
dns     IN      A       172.17.0.1
gitbucket       IN      CNAME   dev
jenkins IN      CNAME   dev
devtop  IN      CNAME   dev
www     IN      CNAME   dev
redmine IN  CNAME   dev

build the docker

docker-compose up -d --build

Confirm docker container is up

root@proj:~# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                                    NAMES
45a961504651        root_dns            "named -c /etc/bin..."   About a minute ago   Up 54 seconds       0.0.0.0:53->53/tcp, 0.0.0.0:53->53/udp   dns
root@proj:~# 

Connect to this container using /bin/sh

docker exec -it dns sh

root:~# docker exec -it dns sh
/ # 
/ # ifconfig 
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02  
          inet addr:172.17.0.2  Bcast:0.0.0.0  Mask:255.255.0.0
...

OK, this container is up as 172.17.0.2.
Check dns works well but alpine's nslookup has bug.
https://bugs.busybox.net/show_bug.cgi?id=675
Nslookup ignored second argument. So I check from host using this container's dns.

root@~# nslookup gitbucket.proj.example.jp 172.17.0.2
Server:     172.17.0.2
Address:    172.17.0.2#53

gitbucket.proj.example.jp   canonical name = dev.proj.example.jp.
Name:   dev.proj.example.jp
Address: 172.17.0.1

nginx

docker-compose.yaml
http:
  container_name: http
  build: http
  volumes:
    - "./http/nginx.conf:/etc/nginx/nginx.conf"
    - "./http/conf.d:/etc/nginx/conf.d"
  ports:
    - "80:80"

mkdir http

http/Dockerfile
FROM alpine:latest
RUN apk --update add nginx && rm -rf /var/cache/apk/*
RUN chmod 755 /var/lib/nginx /var/lib/nginx/tmp
CMD ["nginx", "-g", "daemon off;"]

Alpine linux set permission 700 of /var/lib/nginx as default. When I use reverse proxy with more than 4Kb file, I couldn't work well. The error is below:

open() "/var/lib/nginx/tmp/proxy/1/00/0000000001" failed (13: Permission denied) while reading upstream, client:

That's why chmod 755 ... is needed.

http/nginx.conf
user  nginx;
worker_processes  1;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    server {
        server_name  localhost;
        location / {
            root   html;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
    # add
    include /etc/nginx/conf.d/*.conf;
    client_max_body_size 50m;
}

Delete comment ,<listen> and add <include> ,<client_max_body_size> from default config file.

gitbucket.conf
upstream gitbucket {
        ip_hash;
        server 172.17.0.1:8080 max_fails=3 fail_timeout=30s ;
}

server {
        server_name                             gitbucket gitbucket.proj.example.jp;
        proxy_set_header Host                   $host;
        proxy_set_header X-Forwarded-For        $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host       $host;
        proxy_set_header X-Forwarded-Server     $host;
        location / {
                proxy_pass http://gitbucket;
        }
}
jenkins.conf
upstream jenkins {
        ip_hash;
        server 172.17.0.1:8081 max_fails=3 fail_timeout=30s ;
}

server {
        server_name                             jenkins jenkins.proj.example.jp;
        proxy_set_header Host                   $host;
        proxy_set_header X-Forwarded-For        $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host       $host;
        proxy_set_header X-Forwarded-Server     $host;
        location / {
                proxy_pass http://jenkins;
        }
}

Now it's the time to build.

docker-compose up --build

Building http
Step 1/4 : FROM alpine:latest
 ---> 4a415e366388
Step 2/4 : RUN apk --update add nginx && rm -rf /var/cache/apk/*
 ---> Running in a56458664972
fetch http://dl-cdn.alpinelinux.org/alpine/v3.5/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.5/community/x86_64/APKINDEX.tar.gz
...(skip)...
http    | nginx: [emerg] open() "/run/nginx/nginx.pid" failed (2: No such file or directory)

Hmmm, let me see. Let's start without nginx.

root@~# cd http
root@http# docker ps -a
CONTAINER ID        IMAGE               COMMAND       4f5025285838        root_http           "nginx -g 'daemon ...

docker run -it root_http sh


  1. (Added 2022) Now many distributions release compact version. It's not recommended use Alpine for now to avoid unexpected errors depends on libc uncompatibility.