Developing Environment with docker-compose & alpine 2017

# About this article

This article is renewal version of [Docker Compose + alpineでサーバー一式を構築する](http://qiita.com/drafts/889257c321f98161efaf)[^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.

[^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.


# 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. 

![conoha-gpfx.png](https://qiita-image-store.s3.amazonaws.com/0/16962/827c4e75-b41a-75dc-6167-5419275791ff.png)

change keymap of VM

![conoha-keymap.png](https://qiita-image-store.s3.amazonaws.com/0/16962/050f48d1-3d1a-8123-c827-4984a844fc95.png)


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

```yaml: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```

```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.

```bind: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";
};
```

```bind: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

```yaml: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```

```Dockerfile: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.

```nginx: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.


```nginx: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;
        }
}
```

```nginx: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